一句话题意:给定一个树,树有点权,要求把树的某些边删去,使树变成三个部分,每部分点权值和相等。
我们很容易想到,再读入的时候记录所有点的点权之和,点权除以3是最后权值相等的值。如果不能整除3一定无解,可以结束程序。
那么之后?
我们很自然地想到,状态可以设计为:f[i]为以i为根的子树上的点权和。边界为它自身点权(初值)。
转移也就很自然,f[i]=sigma f[j],j枚举i的儿子。但是很有可能儿子上的权值就满足最后的权值,我们就可以干掉这颗子树,继续遍历当前主树。最后两个断点就先从小子树中找到,再从较大子树中找到。
值得我们注意的是,这的确是一棵有根树,提链的老哥手里攥着的灯泡就是根,根是不能作为断点的。这点需要特别注意。
code
1 #include<cstdio> 2 #include<algorithm> 3 #include<vector> 4 5 using namespace std; 6 7 int n,sum,root; 8 int x=-2,y=-2; 9 int f[1000090]; 10 int val[1000090]; 11 vector<int>son[1000090]; 12 13 void TreeDp(int u,int fa) 14 { 15 f[u]=val[u]; 16 for(int i=0;i<son[u].size();i++) 17 { 18 int v=son[u][i]; 19 if(v==fa) continue; 20 TreeDp(v,u); 21 if(f[v]==sum&&y==-2) y=v; 22 else f[u]+=f[v]; 23 } 24 if(f[u]==sum&&x!=root&&y!=-2) x=u; 25 } 26 27 int main() 28 { 29 scanf("%d",&n); 30 for(int i=1;i<=n;i++) 31 { 32 int u=0,w=0; 33 scanf("%d%d",&u,&w); 34 sum+=w; 35 if(u==0) root=i; 36 val[i]=w; 37 son[u].push_back(i); 38 } 39 // printf("%d ",sum); 40 if(sum%3!=0) 41 { 42 printf("-1"); 43 return 0; 44 } 45 sum/=3; 46 TreeDp(root,-2); 47 if(x!=-2&&x!=root) printf("%d %d",x,y); 48 else printf("-1"); 49 return 0; 50 }