zoukankan      html  css  js  c++  java
  • 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+1le2*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
    ", ans);
        for(RG int i = 1; i <= m; ++i){
            RG int x = Input(), y = Input();
            Modify(x, y);
            printf("%lld
    ", ans);
        }
        return 0;
    }
    
    
  • 相关阅读:
    Django 同步数据库的时候app中的models的表没有成功创建
    mysql 个人博客应用的建表和相关查询
    lambda(),map(),filter()
    用小白鼠找毒药
    python 汉诺塔问题
    灰色预测
    python可视化图标
    exel数据可视化
    543. 二叉树的直径
    236. 二叉树的最近公共祖先
  • 原文地址:https://www.cnblogs.com/cjoieryl/p/8680769.html
Copyright © 2011-2022 走看看