fastle
垆边人似月 皓腕凝霜雪

给出一棵树,给定每一个点的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 << "\n";
    while(m--) {
        int x = read(), v = read();
        lct.modify(x, v);
        cout << ans << "\n";
    }
    return 0;
}

 

posted on 2018-12-13 10:56  fastle  阅读(197)  评论(0编辑  收藏  举报