[动态规划][换根] Codeforces 1292C Xenon's Attack on the Gangs

题目大意

现在有一棵 (n (2leq n leq 3000)) 个结点的树,每条边分配了一个 ([0,2]) 的整数权值 (w) , 所有分配的整数都是不同的,并且每个整数都能分配给某条边。

定义: (S_{path(s,t)}=mex_{(u,v)in path(s,t)} w(u,v))
要求最大化 (sumlimits_{1leq s < t leq n} S_{path(s,t)})

题解

考虑每条边的贡献,我们从0开始分配边权。
假设我们令 (w(u_0,v_0)=0) , 令边 ((u_0,v_0)) 把树分成了两个点集, 其中含有 (u) 的点集的大小是 (Size(u_0)), 含有 (v_0) 的点集的大小是 (Size(v_0))。则令 (w(u_0,v_0)=0) 后,对于所有跨越 ((u_0,v_0)) 的路径,其 (mex) 都大于 (0),贡献是 (Size(u_0) imes Size(v_0))。然后我们去分配边权1,令 (w(u_1,v_1)=1),因为是 (mex),所以此时只对同时经过 ((u_0,v_0))((u_1,v_1)) 的边有贡献。显然,((u_1,v_1)) 应该与 ((u_0,v_0)) 相连才更优。以此类推,我们将最终把一条树链上的每条边分配边权,不在这条树链上的边给它分配边权也对答案没有贡献。且这条链上的边权一定是先减后增,否则易证答案不会更优。

因为链上的边权是先减后增,所以每次是按边权从小到大给链的两端分配边权。这样的话和区间dp有点类似。设 (dp[u][v]) 表示已经给 (usim v) 这条树链分配了边权 (0sim l-1)所得到的最大答案。设(Size_{root}(u)) 表示以 (root) 为根时以 (u) 为根的子树的大小,(Fa_{root}(u)) 表示以 (root) 为根时 (u) 的父亲。那么(dp[u][v]=max(dp[u][Fa_u(v)],dp[Fa_v(u)][v])+Size_u(v) imes Size_v(u)) 。对于(Size_{root}(u))(Fa_{root}(u)),只要从每个点开始跑一遍DFS预处理即可,对于 (dp[u][v]) 记忆化搜索即可。时间复杂度 (O(n^2))

Code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <map>
using namespace std;

#define RG register int
#define LL long long

template<typename elemType>
inline void Read(elemType &T){
    elemType X=0,w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    T=(w?-X:X);
}

struct edge{int Next,to;};
edge G[6010];
int head[3010],Fa[3010][3010];
LL Size[3010][3010],dp[3010][3010];
int N,cnt=2;
LL Ans=0;

inline void Add_Edge(int u,int v){
    G[cnt].to=v;
    G[cnt].Next=head[u];
    head[u]=cnt++;
}

void DFS(int u,int fa,int root){
    Size[root][u]=1;
    for(int i=head[u];i;i=G[i].Next){
        int v=G[i].to;
        if(v==fa) continue;
        Fa[root][v]=u;
        DFS(v,u,root);
        Size[root][u]+=Size[root][v];
    }
    return;
}

LL DP(int u,int v){
    if(u==v) return 0;
    if(dp[u][v]) return dp[u][v];
    return dp[u][v]=max(DP(u,Fa[u][v]),DP(Fa[v][u],v))+Size[u][v]*Size[v][u];
}

int main(){
    Read(N);
    for(RG i=1;i<=N-1;++i){
        int u,v;
        Read(u);Read(v);
        Add_Edge(u,v);
        Add_Edge(v,u);
    }
    for(RG i=1;i<=N;++i)
        DFS(i,0,i);
    for(RG u=1;u<=N;++u)
        for(RG v=1;v<=N;++v)
            Ans=max(Ans,DP(u,v));
    printf("%I64d
",Ans);
    return 0;
}
原文地址:https://www.cnblogs.com/AEMShana/p/12695375.html