Bzoj5212: [Zjoi2018]历史
题面
Sol
以下多数东西都是复制题解的
外省体验赛这题顺利获得了\(10\)分的好成绩。。。
显然是\(Access\)操作,已知每个点\(Access\)的次数,确定一种顺序,问轻重链切换次数的最大值
考虑\(i\)处的切换次数,如果连续两次\(Access\)在同一子树或者都是它自己,那么显然两次\(Access\)之间在\(i\)出不会发生切换
考虑这样一个问题
有\(m\)种颜色的小球,第\(i\)种颜色有\(A_i\)个,要求把所有小球摆成一列,最大化左右小球的颜色不同的间隔数
这个间隔数就是指不同颜色段的切换次数
\(aabbaa\)为两次
\(aabbccaa\)为三次
这个问题答案就是,设\(sum=\sum_{i=1}^{m}A_i\),\(mx=max_{i=1}^{m}Ai\)
则答案为\(min(sum-1,2(sum-mx))\)
当\(sum+1\le2*mx\)时取\(2(sum-mx)\)
这个结论为什么是对的:
当最大值足够小时,一定可以构造出每连续两种颜色都不同的方案,这样一定最大
否则,最大值会多出一部分,它们只能放在一起
那么每次就可以\(O(n)\)求解了,\(Dfs\)就好了
IL void Dfs(RG int u, RG int ff){
RG ll mx; sum[u] = mx = val[u];
for(RG int e = first[u]; e != -1; e = edge[e].next){
RG int v = edge[e].to;
if(v == ff) continue;
Dfs(v, u);
mx = max(mx, sum[v]);
sum[u] += sum[v];
}
ans += min(sum[u] - 1, 2LL * (sum[u] - mx));
}
然后我们考虑修改
用\(LCT\)来维护
首先我们可以类似树剖一样
如果\(sum[fa[u]]\le2sum[u]\),那么这条边是实边
显然每个点只有一条实边
每次修改\(u\)的点权,只会影响到它的祖先们
我们可以写一个类似\(Access\)的操作来维护
维护就是按照小球的那个结论暴力搞一下,然后维护子树信息,维护重儿子
看代码可能就可以懂
# include <bits/stdc++.h>
# define RG register
# define IL inline
# define Fill(a, b) memset(a, b, sizeof(a))
using namespace std;
const int _(4e5 + 5);
typedef long long ll;
IL int Input(){
RG int x = 0, z = 1; RG char c = getchar();
for(; c < '0' || c > '9'; c = getchar()) z = c == '-' ? -1 : 1;
for(; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
return x * z;
}
int n, m, cnt, first[_], fa[_], ch[2][_];
ll sum[_], ans, val[_], sz[_];
struct Edge{
int to, next;
} edge[_ << 1];
IL void Add(RG int u, RG int v){
edge[cnt] = (Edge){v, first[u]}, first[u] = cnt++;
}
IL int Son(RG int x){
return ch[1][fa[x]] == x;
}
IL int Isroot(RG int x){
return ch[0][fa[x]] != x && ch[1][fa[x]] != x;
}
IL void Update(RG int x){
sum[x] = sum[ch[0][x]] + sum[ch[1][x]] + val[x] + sz[x];
}
IL void Rotate(RG int x){
RG int y = fa[x], z = fa[y], c = Son(x);
if(!Isroot(y)) ch[Son(y)][z] = x; fa[x] = z;
ch[c][y] = ch[!c][x], fa[ch[c][y]] = y;
ch[!c][x] = y, fa[y] = x, Update(y);
}
IL void Splay(RG int x){
for(RG int y = fa[x]; !Isroot(x); Rotate(x), y = fa[x])
if(!Isroot(y)) Son(x) ^ Son(y) ? Rotate(x) : Rotate(y);
Update(x);
}
IL void Access(RG int sn, RG int x, RG int v){
for(; x; sn = x, x = fa[x]){
Splay(x);
RG ll ss = val[x] + sz[x] + sum[ch[1][x]];
if(ch[1][x]) ans -= (ss - sum[ch[1][x]]) << 1;
else if(ss < (val[x] << 1)) ans -= (ss - val[x]) << 1;
else ans -= ss - 1;
if(sn != ch[1][x]) sz[x] += v; sum[x] += v, ss += v;
if(ss >= (sum[ch[1][x]] << 1)) sz[x] += sum[ch[1][x]], ch[1][x] = 0;
if(ss < (sum[sn] << 1)) sz[x] -= sum[ch[1][x] = sn];
if(ch[1][x]) ans += (ss - sum[ch[1][x]]) << 1;
else if(ss < (val[x] << 1)) ans += (ss - val[x]) << 1;
else ans += ss - 1;
}
}
IL void Modify(RG int x, RG int v){
Splay(x);
RG ll ss = val[x] + sz[x] + sum[ch[1][x]];
if(ch[1][x]) ans -= (ss - sum[ch[1][x]]) << 1;
else if(ss < (val[x] << 1)) ans -= (ss - val[x]) << 1;
else ans -= ss - 1;
val[x] += v, ss += v, sum[x] += v;
if(ss >= (sum[ch[1][x]] << 1)) sz[x] += sum[ch[1][x]], ch[1][x] = 0;
if(ch[1][x]) ans += (ss - sum[ch[1][x]]) << 1;
else if(ss < (val[x] << 1)) ans += (ss - val[x]) << 1;
else ans += ss - 1;
Access(x, fa[x], v);
}
IL void Dfs(RG int u, RG int ff){
RG ll mxp = u, mx = val[u]; sum[u] = val[u], fa[u] = ff;
for(RG int e = first[u]; e != -1; e = edge[e].next){
RG int v = edge[e].to;
if(v == ff) continue;
Dfs(v, u);
if(sum[v] > mx) mxp = v, mx = sum[v];
sum[u] += sum[v];
}
ans += min(sum[u] - 1, (sum[u] - mx) << 1);
if(mxp != u && (mx << 1) > sum[u]) ch[1][u] = mxp;
sz[u] = sum[u] - val[u] - sum[ch[1][u]];
}
int main(RG int argc, RG char* argv[]){
n = Input(), m = Input();
for(RG int i = 1; i <= n; ++i) val[i] = Input(), first[i] = -1;
for(RG int i = 1, u, v; i < n; ++i) u = Input(), v = Input(), Add(u, v), Add(v, u);
Dfs(1, 0), printf("%lld\n", ans);
for(RG int i = 1; i <= m; ++i){
RG int x = Input(), y = Input();
Modify(x, y);
printf("%lld\n", ans);
}
return 0;
}