题意:在$N$个点的$LCT$中,最开始每条边的虚实不定,给出每一个点的$access$次数,求一种$access$方案使得每条边的虚实变换次数之和最大,需要支持动态增加某个点的$access$次数。$N leq 4 imes 10^5$
ZJOI2018真的都是大火题
首先一个小小的转化:对于每个非叶子节点,新开一个叶子节点,将当前非叶子节点的$access$次数转移到这些叶子节点上,这样所有的$access$操作都在叶子节点进行,可以少很多的判断。
接着我们需要考虑在每一个点上最大化方案总数。因为必须相邻两次$access$由不同子树的叶子节点发起才能够贡献$1$的答案,而我们希望每一次$access$都能尽可能多贡献答案,所以尽可能让相邻两次$access$来自不同子树。考虑树上某一个非叶子节点$x$,其所有儿子为集合$i$,$S$表示子树$access$次数总和,显然答案贡献的最大值与$sum S_i$与$max{S_i}$相关,因为当$max{S_i}$占$sum S_i$比例特别大的时候,则必定要有很多同一子树来的$access$操作被放在一起。
现讲结论吧,最大$access$贡献是
$$min{S_i-1 , 2 imes (sum S_i - max {S_i})}$$
也就是在$max{S_i} > frac{sum S_i + 1}{2}$时总贡献数量会取右边一项
$min$中的左边一项表示的是任意两个$access$之间都产生$1$的贡献(最优的情况),而对于右边的项,因为只有取到最大值的子树的贡献次数变少了,那么我们考虑所有其他子树,它们每一次$access$都可以在这一次$access$的之前、之后的$access$中取到$2$的贡献,所以贡献总和就是右边那一项,树形$DP$计算一次就能获得$30pts$。
接着我们考虑修改操作。如果在某一个点的子树集合$i$上存在一个子树$x$满足$S_x > frac{sum S_i + 1}{2}$,那么如果我们在子树$x$上加上$access$次数$w$,和和最大值同时增加了$w$,也就是说贡献没有变化。我们考虑将满足$S_x > frac{sum S_i + 1}{2}$的点与其父亲连一条实边,表示这一条边连接的两个点之间无需转移,而其他的边就连为轻边。
观察一下条件:$S_x > frac{sum S_i + 1}{2}$,是不是很像重链剖分?其实实质就是重链剖分
然后我们就只需要考虑轻边上的转移了。可以知道每一个点到根的轻边的数量是$log \, sum (access ext{次数})$级别的,复杂度也符合要求。所以可以使用$LCT$动态维护轻重边的划分,外部计算全局答案,每一次找到一条轻边的时候,看能否将其改为重边,去掉以前这个点的贡献,加上当前的贡献即可。
1 #include<bits/stdc++.h> 2 #define int long long 3 #define lch Tree[x].ch[0] 4 #define rch Tree[x].ch[1] 5 #define mid ((Tree[x].sum + 1) >> 1) 6 //This code is written by Itst 7 using namespace std; 8 9 inline int read(){ 10 int a = 0; 11 bool f = 0; 12 char c = getchar(); 13 while(c != EOF && !isdigit(c)){ 14 if(c == '-') 15 f = 1; 16 c = getchar(); 17 } 18 while(c != EOF && isdigit(c)){ 19 a = (a << 3) + (a << 1) + (c ^ '0'); 20 c = getchar(); 21 } 22 return f ? -a : a; 23 } 24 25 const int MAXN = 400010; 26 struct node{ 27 int fa , ch[2] , sum , non_sum; 28 bool type; 29 }Tree[MAXN << 1]; 30 struct Edge{ 31 int end , upEd; 32 }Ed[MAXN << 2]; 33 int a[MAXN] , head[MAXN] , sum , N , cntEd; 34 35 inline bool nroot(int x){ 36 return Tree[Tree[x].fa].ch[0] == x || Tree[Tree[x].fa].ch[1] == x; 37 } 38 39 inline bool son(int x){ 40 return Tree[Tree[x].fa].ch[1] == x; 41 } 42 43 inline void pushup(int x){ 44 Tree[x].sum = Tree[lch].sum + Tree[rch].sum + Tree[x].non_sum + (x > N ? a[x - N] : 0); 45 } 46 47 inline void rotate(int x){ 48 bool f = son(x); 49 int y = Tree[x].fa , z = Tree[y].fa , w = Tree[x].ch[f ^ 1]; 50 if(nroot(y)) 51 Tree[z].ch[son(y)] = x; 52 Tree[x].fa = z; 53 Tree[x].ch[f ^ 1] = y; 54 Tree[y].fa = x; 55 Tree[y].ch[f] = w; 56 if(w) 57 Tree[w].fa = y; 58 pushup(y); 59 } 60 61 inline void Splay(int x){ 62 while(nroot(x)){ 63 if(nroot(Tree[x].fa)) 64 rotate(son(x) == son(Tree[x].fa) ? Tree[x].fa : x); 65 rotate(x); 66 } 67 pushup(x); 68 } 69 70 inline void access(int x , int w){ 71 a[x - N] += w; 72 Splay(x); 73 while(Tree[x].fa){ 74 Splay(Tree[x].fa); 75 int k = Tree[x].fa , t = Tree[k].sum - Tree[Tree[k].ch[0]].sum; 76 if(Tree[k].ch[1]) 77 sum -= (t - Tree[Tree[k].ch[1]].sum) << 1; 78 else 79 sum -= t - 1; 80 Tree[k].non_sum += w; 81 Tree[k].sum += w; 82 t += w; 83 if(Tree[Tree[k].ch[1]].sum < Tree[x].sum){ 84 Tree[k].non_sum = Tree[k].non_sum - Tree[x].sum + Tree[Tree[k].ch[1]].sum; 85 Tree[k].ch[1] = x; 86 } 87 if(((t + 1) >> 1) < Tree[Tree[k].ch[1]].sum) 88 sum += (t - Tree[Tree[k].ch[1]].sum) << 1; 89 else{ 90 sum += t - 1; 91 Tree[k].non_sum += Tree[Tree[k].ch[1]].sum; 92 Tree[k].ch[1] = 0; 93 } 94 x = k; 95 } 96 } 97 98 inline void addEd(int a , int b){ 99 Ed[++cntEd].end = b; 100 Ed[cntEd].upEd = head[a]; 101 head[a] = cntEd; 102 } 103 104 void dfs(int x , int fa){ 105 Tree[x].fa = fa; 106 if(x > N) 107 return; 108 for(int i = head[x] ; i ; i = Ed[i].upEd) 109 if(Ed[i].end != fa){ 110 dfs(Ed[i].end , x); 111 Tree[x].sum += Tree[Ed[i].end].sum; 112 } 113 for(int i = head[x] ; i ; i = Ed[i].upEd) 114 if(Ed[i].end != fa && mid < Tree[Ed[i].end].sum){ 115 sum += (Tree[x].sum - Tree[Ed[i].end].sum) << 1; 116 Tree[x].non_sum = Tree[x].sum - Tree[Ed[i].end].sum; 117 Tree[x].ch[1] = Ed[i].end; 118 return; 119 } 120 sum += Tree[x].sum - 1; 121 Tree[x].non_sum = Tree[x].sum; 122 } 123 124 signed main(){ 125 #ifndef ONLINE_JUDGE 126 freopen("4338.in" , "r" , stdin); 127 //freopen("4338.out" , "w" , stdout); 128 #endif 129 N = read(); 130 int M = read(); 131 for(int i = 1 ; i <= N ; ++i) 132 Tree[i + N].sum = a[i] = read(); 133 for(int i = 1 ; i < N ; ++i){ 134 int a = read() , b = read(); 135 addEd(a , b); 136 addEd(b , a); 137 } 138 for(int i = 1 ; i <= N ; ++i) 139 addEd(i , i + N); 140 dfs(1 , 0); 141 printf("%lld " , sum); 142 while(M--){ 143 int a = read() , x = read(); 144 access(a + N , x); 145 printf("%lld " , sum); 146 } 147 return 0; 148 }