给出一棵树,给定每一个点的accessaccess次数,计算轻重链切换次数的最大值,带修改.
/* 想必大佬是在分析lct复杂度 的时候出出来的这道题耶 zjoi果然都是神题 考虑对于每个点, 他被两次操作更改了的条件, 是这相邻两次在他子树中的操作access的点位于它的不同子树中 那么我们现在只考虑一个点, 怎样安排 他的子树能让他的贡献最大 , 这样就引出了一个新问题, 对于有长度为m的序列, 和m个小球, 每种颜色有ai个 最大化相邻不同颜色 设颜色最多的那个数量为mx, 那么答案就是min(m - 1, 2 * (m - mx)) 然后我们惊奇地发现, 每个点之间是互不影响的, 那么维护对于每个点的 sum 和m最大子树值就可以Ondp一次拿到30分好成绩了 然后就考虑修改了 修改相当于给i到1的路径上所有的sum 加上i, 然后mx可能改可能不改 然后这里就是利用 神奇的性质了, 对于树进行类似于重链剖分的构造, 设置实边和虚边, 定义一个点向满足 m - 1《 2 * (m - mx)的孩子连实边, 发现每个点最多会连出一条实边 连出然后通过式子发现, 假如是实边的子树中发生了更改的话, 是不会对答案造成影响的, 而轻边的影响可以暴力算出来, 在整个过程中, 可能会有虚边变成实边, 然后根据什么神奇定义, 一个点到跟的虚边个数不超过log, 所以动态树的思想动态维护实虚边即可 细节贼多 */ #include<cstdio> #include<algorithm> #include<cstring> #include<iostream> #include<queue> #include<map> #include<set> #include<cmath> #define M 400010 #define ll long long using namespace std; int read() { int nm = 0, f = 1; char c = getchar(); for(; !isdigit(c); c = getchar()) if(c == '-') f = -1; for(; isdigit(c); c = getchar()) nm = nm * 10 + c - '0'; return nm * f; } int n, m; ll ans, a[M]; ll clac(ll t, ll h) { return t ? min(2 * (t - h), t - 1) : t; } struct L { #define ls ch[x][0] #define rs ch[x][1] #define isr(x) (ch[fa[x]][1] != x && ch[fa[x]][0] != x) int ch[M][2], fa[M], tot; ll ver[M], sum[M], vsum[M]; void pushup(int x) { sum[x] = sum[ls] + sum[rs] +ver[x] + vsum[x]; } void rotate(int x) { int y = fa[x], q = fa[y]; bool dy = (ch[y][1] == x), dz = (ch[q][1] == y); if(!isr(y)) ch[q][dz] = x; fa[x] = q; fa[ch[x][dy ^ 1]] = y; ch[y][dy] = ch[x][dy ^ 1]; ch[x][dy ^ 1] = y; fa[y] = x; pushup(y); pushup(x); } void splay(int x) { while(!isr(x)) { int y = fa[x], q = fa[y]; if(!isr(y)) { if((ch[q][1] == y) ^ (ch[y][1] == x)) rotate(x); else rotate(y); } rotate(x); } } void modify(int x, ll v) { splay(x); ver[x] += v; pushup(x); ll tmp = sum[x] - sum[ls]; if(rs) { ll hson = sum[rs]; ans += clac(tmp, max(hson, ver[x])) - clac(tmp - v, hson); if(hson * 2 < tmp + 1) { vsum[x] += hson; ch[x][1] = 0; } } else ans += clac(tmp, ver[x]) - clac(tmp - v, ver[x] - v); pushup(x); // cout << x << " "; int y; for(y = x, x = fa[x]; x; y = x, x = fa[x]) { splay(x); vsum[x] += v; pushup(x); tmp = sum[x] - sum[ls]; if(rs) { ll hson = sum[rs]; ans += clac(tmp, max(hson, sum[y])) - clac(tmp - v, hson); if(hson * 2 < tmp + 1) { vsum[x] += hson; rs = 0; } } else ans += clac(tmp, max(ver[x], sum[y])) - clac(tmp - v, ver[x]); if(sum[y] * 2 >= tmp + 1) { vsum[x] -= sum[y]; rs = y; } pushup(x); } } } lct; vector<int> to[M]; void dfs(int now, int fa) { for(int i = 0; i < to[now].size(); i++) { int vj = to[now][i]; if(vj == fa) continue; lct.fa[vj] = now; dfs(vj, now); } } int main() { //freopen(".in", "r", stdin), freopen(".out", "w", stdout); n = read(), m = read(); for(int i = 1; i <= n; i++) a[i] = read(); for(int i = 1; i < n; i++) { int vi = read(), vj = read(); to[vi].push_back(vj); to[vj].push_back(vi); } dfs(1, 1); for(int i = 1; i <= n; i++) lct.modify(i, a[i]); cout << ans << " "; while(m--) { int x = read(), v = read(); lct.modify(x, v); cout << ans << " "; } return 0; }