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

题目大意

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

定义: \(S_{path(s,t)}=mex_{(u,v)\in path(s,t)} w(u,v)\)
要求最大化 \(\sum\limits_{1\leq 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)\times 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]\) 表示已经给 \(u\sim v\) 这条树链分配了边权 \(0\sim 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)\times 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\n",Ans);
    return 0;
}
posted @ 2020-04-14 01:21  AE酱  阅读(111)  评论(0编辑  收藏  举报