洛谷 P1131 [ZJOI2007]时态同步(树形dp,贪心)

传送门


解题思路

唔,我才不要上网课呢

很显然对于每个节点i,必须保证叶子节点到这个点的传送距离相等,才能保证最终答案都是相等,而最少的增加次数一定就是使距离全部变成其中最大的距离。

好吧,解释的不清楚。

如果没听懂,重新用贪心来解释一下。

先想一下最终距离。设最终距离为d。

最终的距离一定就等于叶子节点到根节点的距离的最大值。

设节点u有两个儿子i、j(均为叶子节点),要是它们到根节点的距离相等且等于最终距离,首先必须保证它们到点u距离相等,为了使用最小的次数,这个距离一定就是i和j离点u较远的距离的d1,具体操作就是把另一个点加上(d1-d2),然后再把点u到根节点的距离变成(d-d1),这样就能保证使用最小的次数。

再推广到节点u有n个儿子(均为叶子节点),很显然,一定要把它们到u的距离全部改为离u点最远的距离d1,然后把其他所有的儿子全部加上一个数,使得它们到点u的距离都变成d1,然后再把u点到根节点的距离变成(d-d1)。

在推广到节点u有n个儿子(不一定为叶子节点),很显然,在我们已经求出以u为根的子树中的每一个叶子节点到点u的最终距离后,取一个max为d1,把其他的儿子加上(d1-到点u的距离),然后再把点u到根节点的距离变成(d-d1)即可。

所以按照树形dp的写法,用dp[i]表示以i为根的子树中所有的叶子节点到点i的最终距离是多少,然后可以用dp[v](v为i的儿子)来更新dp[i]的值。

然后关于答案的求法就是在求dp中(改变距离时)加上。

好吧,貌似翻车了

还是看代码吧

AC代码

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int maxn=500005;
 6 int n,s,c[maxn],p[maxn],cnt,dp[maxn];
 7 long long ans;
 8 struct node{
 9     int v,next,value;
10 }e[maxn*2];
11 void insert(int u,int v,int value){
12     cnt++;
13     e[cnt].v=v;
14     e[cnt].value=value;
15     e[cnt].next=p[u];
16     p[u]=cnt;
17 }
18 void dfs(int u,int fa){
19     if(u!=s&&c[u]==1){
20         dp[u]=0;
21         return;
22     }
23     for(int i=p[u];i!=-1;i=e[i].next){//求dp
24         int v=e[i].v;
25         if(v==fa) continue;
26         dfs(v,u);
27         dp[u]=max(dp[u],dp[v]+e[i].value);
28     }
29     for(int i=p[u];i!=-1;i=e[i].next){//求答案
30         int v=e[i].v;
31         if(v==fa) continue;
32         ans+=dp[u]-(dp[v]+e[i].value);
33     }
34 }
35 int main()
36 {
37     memset(p,-1,sizeof(p));
38     cin>>n>>s;
39     for(int i=1;i<n;i++){
40         int u,v,value;
41         scanf("%d%d%d",&u,&v,&value);
42         insert(u,v,value);
43         insert(v,u,value);
44         c[u]++;
45         c[v]++;//求叶子节点
46     }
47     dfs(s,-1);
48     cout<<ans;
49     return 0;
50 }

//ZJOI2007 Day1 t4

posted @ 2020-03-08 10:42  尹昱钦  阅读(209)  评论(0编辑  收藏  举报