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;
}

posted @ 2018-03-31 08:04  Cyhlnj  阅读(327)  评论(3编辑  收藏  举报