[ZJOJ2007]时态同步 贪心

不是很懂为什么luogu标签是树形DP,感觉我想的就是一个贪心啊。。。

随机造几组数据,我们发现贪心的确可以得到最优解,那么为什么呢?

假设将所有时态贪心的调整是对的,
那么如果一个节点的各个儿子时态不同,那么强行统一,
为什么可以假设是对的?
因为观察到在一个点的上方+1,对它的子树的相对关系的没有影响的,
因此子树里面的时态同步只能在内部做,所以一步一步统一上来,
而且由于如果是要改变两个子树之间的大小关系的话,
因为是整个子树的修改,同时不影响到其他子树,因此这个时候就是在一个点的上方+1了,
同时因为每次只考虑子树内部的关系,所以整个子树的修改会被延后到上一个节点,
这时两个子树之间的修改就变成了子树内部的修改,因此递归上去即可

注意:由于f[x]中存的子树个数在不断增加,因此f[x]中的个数可能不只一个,因此如果要把f[x]中的子树增加到f[now]代表的子树的话,

因为路径不同,因此要修改cnt(f[x]中的子树个数)条路径,并且每条路径都是+相同的数,所以代价要*cnt

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 /*假设将所有时态贪心的调整是对的,
 4 那么如果一个节点的各个儿子时态不同,那么强行统一,
 5 为什么可以假设是对的?
 6 因为观察到在一个点的上方+1,对它的子树的相对关系的没有影响的,
 7 因此子树里面的时态同步只能在内部做,所以一步一步统一上来,
 8 而且由于如果是要改变两个子树之间的大小关系的话,
 9 因为是整个子树的修改,同时不影响到其他子树,因此这个时候就是在一个点的上方+1了,
10 同时因为每次只考虑子树内部的关系,所以整个子树的修改会被延后到上一个节点,
11 这时两个子树之间的修改就变成了子树内部的修改*/
12 #define R register int
13 #define AC 1001000
14 #define D printf("line in %d
",__LINE__);
15 #define LL long long
16 int n,root;
17 int date[AC],Next[AC],Head[AC],value[AC],tot;
18 LL ans,have;
19 LL f[AC];//f[i]代表节点i的子树内的时态已经被统一为了f[i]
20 bool z[AC],vis[AC];
21 inline int read()
22 {
23     int x=0;char c=getchar();
24     while(c > '9' || c < '0') c=getchar();
25     while(c >= '0' && c <= '9') x=x*10+c-'0',c=getchar();
26     return x;
27 }
28 
29 inline void add(int f,int w,int S)
30 {
31     date[++tot]=w,Next[tot]=Head[f],value[tot]=S,Head[f]=tot;
32     date[++tot]=f,Next[tot]=Head[w],value[tot]=S,Head[w]=tot;
33 }
34 
35 inline void pre()
36 {
37     R a,b,c;
38     n=read(),root=read();
39     for(R i=1;i<n;i++)
40     {
41         a=read(),b=read(),c=read();
42         add(a,b,c);
43     }
44 }
45 
46 void DFS(int x)//先统计一下
47 {
48     vis[x]=true;
49     R now;
50     bool done=false;
51     for(R i=Head[x]; i ;i=Next[i])
52     {
53         now=date[i];
54         if(vis[now]) continue;
55         if(!done) done=true;
56         have+=value[i];
57         DFS(now);
58         have-=value[i];
59     }
60     if(!done) f[x]=have;
61 }
62 
63 void DP(int x)
64 {
65     z[x]=true;
66     R now,cnt=0;
67     for(R i=Head[x]; i ;i=Next[i])
68     {
69         now=date[i];
70         if(z[now]) continue;
71         DP(now);
72         if(!f[x]) f[x]=f[now];
73         else if(f[now] != f[x])
74         {
75             if(f[now] < f[x]) ans+=f[x] - f[now];
76             else ans+=(f[now] - f[x]) * cnt,f[x]=f[now];
77         }//error!!!因为f[now]只代表新遍历到的子树,而f[x]则可能代表很多个子树
78         ++cnt;
79     }//error!!!于是如果统计让很多个子树暴力修改上来的代价,因为要修改很多边,因此要*cnt(f[x]中的子树个数)        
80 }
81 
82 int main()
83 {
84     freopen("in.in","r",stdin);
85     pre();
86     DFS(root);
87     DP(root);
88     printf("%lld
",ans);
89     fclose(stdin);
90     return 0;
91 }
原文地址:https://www.cnblogs.com/ww3113306/p/8868402.html