【POJ3585】Accumulation Degree
有关树形dp+二次扫描与换根法
首先我们读入数据,用邻接表存储,并记录每一个点相连的边的数量。
我们不妨先暂时假定节点1为树根,定义d[x]表示在以x为根的子树当中最大的流量,显然得出状态转移方程
d[x]=∑min(d[y],c(x,y)) 其中y∈son(x).
求出了d数组,我们考虑“二次扫描与换根法”,定义f[x]表示以x为这棵树的根时的最大流量。那么f[x]包括两部分:
- 以x为根,流向x的儿子们的流量,这部分的答案即为d[x]
- 从x流向x的父亲,进而流向其他的节点,这一部分的答案我们可以这样考虑:把x作为根时流量为f[x],从x流向x的儿子y的流量为min(d[y],c(x,y)),所以x的流量减去这部分流量得到的差值就是剩余部分的流量。于是,把y作为原点,从y流向x的流量就是这个差值与c(x,y)取最小值的结果。因此不难得出状态转移方程f[y]=d[y]+min(f[x]-min(d[y],c(x,y)),c(x,y))
1 #include <iostream> 2 #include <cstdio> 3 #include <stack> 4 #include <algorithm> 5 #include <queue> 6 #include <cstring> 7 #include <cmath> 8 #define maxx 200005 9 using namespace std; 10 int head[maxx],to[maxx<<1],w[maxx<<1],_next[maxx<<1]; 11 int d[maxx]; 12 int cnt=0; 13 int deg[maxx]; 14 void addEdge(int x,int y,int _w) { 15 to[++cnt]=y,w[cnt]=_w,_next[cnt]=head[x],head[x]=cnt; 16 to[++cnt]=x,w[cnt]=_w,_next[cnt]=head[y],head[y]=cnt; 17 } 18 int n; 19 void dfs1(int root,int fa) { 20 d[root]=0; 21 for(int i=head[root]; i; i=_next[i]) { 22 int v=to[i]; 23 if(v==fa) 24 continue; 25 dfs1(v,root); 26 if(deg[v]==1) 27 d[root]+=w[i]; 28 else 29 d[root]+=min(w[i],d[v]); 30 } 31 } 32 int f[maxx]; 33 void dfs2(int root,int fa) { 34 for(int i=head[root]; i; i=_next[i]) { 35 int v=to[i]; 36 if(v==fa) 37 continue; 38 if(deg[root]==1) 39 f[v]=d[v]+w[i]; 40 else 41 f[v]=d[v]+min(f[root]-min(w[i],d[v]),w[i]); 42 dfs2(v,root); 43 } 44 } 45 void init() { 46 memset(head,0,sizeof(head)); 47 memset(f,0,sizeof(f)); 48 memset(deg,0,sizeof(deg)); 49 cnt=0; 50 } 51 int main() { 52 int t; 53 cin>>t; 54 while(t--) { 55 cin>>n; 56 int x,y,w; 57 init(); 58 for(int i=1; i<n; i++) { 59 scanf("%d%d%d",&x,&y,&w); 60 deg[x]++; 61 deg[y]++; 62 addEdge(x,y,w); 63 } 64 if(n==0||n==1) { 65 cout<<0<<endl; 66 continue; 67 } 68 int root=1; 69 dfs1(root,0); 70 f[root]=d[root]; 71 dfs2(root,0); 72 int ans=0; 73 for(int i=1; i<=n; i++) 74 ans=max(ans,f[i]); 75 cout<<ans<<endl; 76 } 77 return 0; 78 }