[LOJ2434] 「ZJOI2018」历史(DP+LCT)

题意

  • 给你一颗树上每个点的accessaccess次数,计算轻重链切换次数的最大值。

首先要发现一个性质,那就是一个点的选择是无后效性的,就是对于每个点贡献的时候,只跟它的所有子树的大小有关,然后每个点的贡献就可以这样算fu=valu+fav=xfvf_u=val_u+\sum_{fa_v=x}f_v,其中valu=min(Sizeu1,(SizeumaxSizev)×2)val_u=min(Size_u-1,(Size_u-maxSize_v)×2)这个东西举几个栗子自己感性理解一下就好了,那么这样子我们就可以获得不带修改的30pts30pts了。

现在我们来考虑带修改怎么做,我们定义一个点的所有儿子中,满足2×Sizev>Sizeu+12×Size_v>Size_u+1的儿子为实儿子,显然这样的儿子只有一个,或者没有。我们类比树链剖分复杂度的证明可以发现,一个点到根节点只有logn\log n条轻边,那么我们可以用LinkCutTreeLinkCutTree来维护这棵树,每次修改的时候暴力往上找到所有轻边即可,为什么这样复杂度是对的呢,因为题目的修改只有给一个权值加上一个数,那么如果它原来是实儿子现在仍旧是实儿子而不会变成虚儿子,那么这样子一个点的实儿子变化的时候就不用去遍历它的每一个儿子来考虑,因为它只能从虚儿子变成实儿子,那么就可以了,复杂度O(nlogn)O(n\log n)

有一些需要注意的地方,就修改的时候为了方便把当前点的贡献存下来,更新的时候减去当前点贡献加上修改后的贡献,还有就是要维护一个点子树和减去实儿子和自己权值的值,因为这样的话可以快速维护修改,又因为一个点在lctlct中的左儿子是在原树中自己的祖先,所以计算子树和的时候一定要减去左儿子权值,然后就是裸的模板了。

#include <bits/stdc++.h>

#define pb push_back
#define ll long long
#define ls(x) (ch[x][0])
#define rs(x) (ch[x][1])
#define sx (S[x] - S[ls(x)]) 
#define rel(x) (x == rs(fa[x]))
#define isroot(x) (ls(fa[x]) ^ x && rs(fa[x]) ^ x)

using namespace std;

const int N = 4e5 + 10;

vector<int> G[N];

ll ans, S[N], Rs[N], res[N], a[N];

int ch[N][2], fa[N], n, q; 

void pushup(int x) {
    S[x] = S[ls(x)] + S[rs(x)] + Rs[x] + a[x];
}

void rotate(int x) {
    int dad = fa[x], f = rel(x);
    if (!isroot(dad)) ch[fa[dad]][rel(dad)] = x; 
    fa[x] = fa[dad], ch[dad][f] = ch[x][f ^ 1];
    fa[ch[dad][f]] = dad, ch[x][f ^ 1] = dad;
    pushup(dad), pushup(fa[dad] = x);
}

void splay(int x) {
    for (; !isroot(x); rotate(x)) if (!isroot(fa[x]))
        rotate(rel(x) == rel(fa[x]) ? fa[x] : x);
}

void update(int x) {
    ans -= res[x]; 
    res[x] = rs(x) ? (sx - S[rs(x)]) * 2 : sx - 1;
    if ((a[x] << 1) > sx + 1) res[x] = (sx - a[x]) << 1; 
    ans += res[x];
}

void access(int x, int z) {
    for (int y = 0; x; update(x), x = fa[y = x]) {
        splay(x), S[x] += z, *((y ? Rs : a) + x) += z;
        if ((S[rs(x)] << 1) < sx + 1) 
            Rs[x] += S[rs(x)], rs(x) = 0;
        if ((S[y] << 1) > sx + 1) 
            rs(x) = y, Rs[x] -= S[rs(x)];
    }
    printf("%lld\n", ans);
}

void dfs(int x, int dad) {
    int now = 0; fa[x] = dad, S[x] = a[x];
    for (auto v : G[x]) if (v ^ dad) {
        dfs(v, x), S[x] += S[v];
        if (S[v] > S[now]) now = v; 
    }
    if ((S[now] << 1) > S[x] + 1) rs(x) = now; 
    Rs[x] = S[x] - a[x] - S[rs(x)], update(x);
}

int main() {
#ifdef ylsakioi
    freopen("2434.in", "r", stdin);
    freopen("2434.out", "w", stdout);
#endif

    int x, y; 

    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; ++ i) 
        scanf("%lld", &a[i]);
    for (int i = 1; i < n; ++ i) {
        scanf("%d%d", &x, &y);
        G[x].pb(y), G[y].pb(x);
    }

    dfs(1, 0), printf("%lld\n", ans);
    while (q --) scanf("%d%d", &x, &y), access(x, y);

    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章