CF767C Garland 【树形dp】By cellur925

一句话题意:给定一个树,树有点权,要求把树的某些边删去,使树变成三个部分,每部分点权值和相等。

我们很容易想到,再读入的时候记录所有点的点权之和,点权除以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\n",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 }
View Code

猜你喜欢

转载自www.cnblogs.com/nopartyfoucaodong/p/9457341.html