Codeforces 835F Round #427 Div2F :树形DP

来源:互联网 发布:mac 解压缩rar 编辑:程序博客网 时间:2024/06/04 22:34

本题同NOI2013快餐店。

题意:给出一张无向图,其中有n个点,n条边,(n<=2e5)没有自环和重边。求去掉一条边形成一棵树之后,树的直径的最小值。

题解:首先这个图是一个带环外向树的图。就是一个环,然后以某些环上的点为根可以延伸出一棵树。那么生成树的个数等于环上的边数,题目意思就是快速的求出这些生成树的直径。那么先考虑如果某个生成树的直径在外向树上面,那么可以很容易的DP求出外向树直径。因为外向树上面的边是不能够删除的,因此外向树完整的被包括在任何一个生成树中,所以答案至少是外向树上直径最大值。

继续考虑第二类直径:直径的一部分在还上。那么这个直径可以分成三部分:第一段是某个外向树的高度(外向树最深的点到根的距离),第二段是环上的一段,第三段也是一个外向树的高度。那么我们显然可以通过枚举删掉的边,然后求这样三段的一个最大值得到该树的直径(伪直径,因为去掉了直径完全在外向树上的情况) 然后取最小来得到正确答案,但是复杂度太高。

问题回到环上,既然是带环的问题,通常的思路是,去掉任意一条边,把他变成一个链再做考虑。那么假设我们去掉 x 这个环上的边不做考虑,那么我们得到了一个链,同时这个链每个点有一个权重=外向树的最深deep。那么根据我们上边说的这个伪直径的形状:一个权重+一段弧+一个权重。

假如我们去掉的边就是x,那么这棵树上的伪直径都是完整的呈现在链上:通过一个DP可以求得他们的最大值,扫一遍就行了,因为现在已经破开了环,变成了线性的问题,略去不讲(提示:伪直径 = max(弧长+权重)+ max(权重-弧长)=max(权重+弧长-弧长+权重)=max(权重+两个权重之间的弧长+权重))。

假如去掉的边不是x,讨论伪直径的形态:第一、不包括x,这样的伪直径在链上也是以完整形态表现出来,其实在前边已经算过了,不需要再考虑;第二、包括x:那么这个伪直径分为三部分:一段弧+一个权重、x、一段弧长+一个权重,而且这两个(弧长+权重)分布在x的左右两边,也就是顺时针、逆时针两个方向上。那么我们可以在 *链上* 按照和上边相同的思路做两个DP,处理出从x开始,分别顺时针、逆时针的(弧长+权重)的一组dp值。然后再扫一遍,线性枚举一下答案。同时,其实有第三种:弧长+权重,这个其实可以看作 0权重+弧长+权重,和第二类是一样的。


代码说明:best[] 和 best1[] 是计算 两个方向上的 伪直径的最大值的dp数组。good[] 和good1[]是计算 两个方向上的 (弧长+权重)的dp数组。lastLength=断开环的那条边长。

circleNode[]按顺序记录了环上的点。circleLength[]按顺序记录了环上每条边,和circleNode是一一对应。

Code:

#include<bits/stdc++.h>#define INF 0x3f3f3f3f#define LL long longusing namespace std;const int MAX = 200050;struct Edge{int des;LL length;Edge(int des_,int length_):des(des_),length(length_){}};vector<Edge>E[MAX];int n;bool vis[MAX];bool inCircle[MAX];int circleNode[MAX];int circleNum;LL circleLength[MAX];int father[MAX];LL father_length[MAX];LL f[MAX];void dfs_find(int x,int fa){vis[x] = true;for (Edge temp:E[x]){int v = temp.des;int length = temp.length;if (v==fa) continue;if (vis[v]){if (inCircle[v]){continue;}int u = x;while (u!=v){inCircle[u] =true;circleNode[++circleNum] = u;circleLength[circleNum] = father_length[u];u = father[u];}inCircle[v] = true;circleNode[++circleNum] = v;circleLength[circleNum] = length;continue;}father[v] = x;father_length[v] = length;dfs_find(v,x);}}LL chain=-1;void dfs_dp(int x,int fa){for (Edge temp :E[x]){int v = temp.des;LL length = temp.length;if (v==fa||inCircle[v]){continue;}dfs_dp(v,x);chain = max(chain,f[x]+f[v]+length);f[x] = max(f[x],f[v]+length);}}LL good[MAX],good1[MAX],best[MAX],best1[MAX];int main(){scanf("%d",&n);for (int i=0;i<n;i++){int u,v,l;scanf("%d%d%d",&u,&v,&l);E[u].push_back(Edge(v,l));E[v].push_back(Edge(u,l));}dfs_find(1,0);//cout<<"OK"<<endl;for (int i=1;i<=circleNum;i++){dfs_dp(circleNode[i],0);}LL sum = 0;LL support =-INF;//cout<<"YES"<<endl;for (int i=1;i<=circleNum;i++){sum+=circleLength[i-1];good[i] = max(good[i-1],sum+f[circleNode[i]]);best[i] = max(best[i-1],sum+f[circleNode[i]]+support);support = max(support,f[circleNode[i]]-sum);}sum = -circleLength[circleNum];support = -INF;for (int i=circleNum;i>=1;i--){//cout<<i<<endl;sum+=circleLength[i];good1[i] = max(good1[i+1],sum+f[circleNode[i]]);best1[i] = max(best1[i+1],sum+f[circleNode[i]]+support);support = max(support,f[circleNode[i]]-sum);}//cout<<"YES"<<endl;LL ans = best[circleNum];int lastLength = circleLength[circleNum];/*printf("last:%d\n",lastLength);for (int i=1;i<=circleNum;i++){printf("good[%d]=%d ",i,good[i]);printf("good1[%d]=%d ",i,good1[i]);printf("best[%d]=%d ",i,best[i]);printf("best1[%d]=%d\n",i,best1[i]);}*/for (int i=1;i<circleNum;i++){LL temp = good[i]+good1[i+1]+lastLength;temp = max(max(best[i],best1[i+1]),temp);ans = min(ans,temp); }ans = max(chain,ans);printf("%I64d\n",ans);return 0;}


原创粉丝点击