• [ZJOI2018]历史


    给出一棵树,给定每一个点的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;
    }
  • 相关阅读:
    土豆网自动播放代码
    js倒计时小插件(兼容大部分浏览器)
    带按钮的网页播放器代码(附文件)
    列出目录下所有文件
    day19 进度条 & 随机验证码
    day18 json与pickle
    day14.2_三元表达式、列表生成式
    day14.1_生成器
    day13_迭代器
    day12.2_完善装饰器
  • 原文地址:https://www.cnblogs.com/luoyibujue/p/10112766.html
Copyright © 2020-2023  润新知