[BZOI1131] 某科学的树形DP
前言
这个题目仅仅是因为我找了半天也没找到这个神奇题目的名字而瞎起了一个,题目过于简单,题意过于明了,简直像是某信奥书上讲解树形DP知识点用的模板。就这么令人无语。
题目
给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大
Input
给出一个数字N,代表有N个点.N<=1000000 下面N-1条边.
Output
输出你所找到的点,如果具有多个解,请输出编号最小的那个.
Sample Input
8
1 4
5 6
4 5
6 7
6 8
2 4
3 4
Sample Output
7
解说
看了第一遍:这题简单过了吧,直接白给。不会有什么陷阱吧……
看了第二遍:没有,真就白给。
既然每个点都可能是根节点,那我先找个1号节点算算试试。下面这是以1为根的样例。下面是以5为根的图,后面要用。
显然f[1]=18。
那么假如我把根节点换了呢?比如换成5号。
对于5号节点的子树(包括自己),由于离根节点更近了一步,所以每节点的深度都要减一,而对于除了5的子树之外的节点,由于离根节点远了一步,深度都要加一,这样的话f [5] = f [1] - 1 * size [5] + 1 * ( n -size [5] )。
同理,5的儿子也可以这么推下去。那么,我们得到一个通式:
f [儿子] = f [父亲] - 1 * size [儿子] + 1 * ( n -size [儿子] )
这样的话,我们跑两遍DFS,第一遍求f[1]和各个点的size,第二遍跑通式,求出最大值即可。
P.S. 如果非要说什么易错点,就是要开long long。最坏的情况就是点最多且排成一条链,这时深度和=(0+999999)*1000000/2=499999500000超int了。
P.S.S. 最开始我还纠结了许久根节点深度是1还是0最后发现好像无所谓……
P.S.S.S. 显然P.S.里面的最大值是按根节点深度为0算的。
代码
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int maxn=1000000+5; 4 typedef long long ll; 5 int head[maxn],ans,size[maxn],n,tot; 6 ll f[maxn],Max; 7 struct node{ 8 int to,next; 9 }e[2*maxn]; 10 void Add(int a,int b){ 11 e[tot].to=b; 12 e[tot].next=head[a]; 13 head[a]=tot; 14 tot++; 15 } 16 void dfs1(int x,int fa,int dep){ 17 size[x]=1; 18 for(int i=head[x];i;i=e[i].next){ 19 int v=e[i].to; 20 if(v==fa) continue; 21 dfs1(v,x,dep+1); 22 size[x]+=size[v]; 23 f[1]+=dep+1; 24 } 25 } 26 void dfs2(int x,int fa){ 27 for(int i=head[x];i;i=e[i].next){ 28 int v=e[i].to; 29 if(v==fa) continue; 30 f[v]=f[x]-size[v]+n-size[v]; 31 if(f[v]>Max){ 32 Max=f[v]; 33 ans=v; 34 } 35 if(f[v]==Max&&v<ans) ans=v; 36 dfs2(v,x); 37 } 38 } 39 int main(){ 40 tot=1; 41 scanf("%d",&n); 42 for(int i=1;i<=n-1;i++){ 43 int x,y; 44 scanf("%d%d",&x,&y); 45 Add(x,y); 46 Add(y,x); 47 } 48 dfs1(1,0,0); 49 Max=f[1]; ans=1; 50 dfs2(1,0); 51 printf("%d",ans); 52 return 0; 53 }
幸甚至哉,歌以咏志。