洛谷 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