• 「ZJOI2018」历史(找性质+重链剖分+lct)


    https://loj.ac/problem/2434

    先考虑(a[i])确定时,答案怎么求?

    (s[x])表示(x)为根的子树的(a)的和。

    对于每一个(x)为根的子树,假设确定了(x)的儿子的子树的答案,接下来合并,那么显然是尽可能插空合并。

    (max=max(a[x],s[y1],s[y2]…)),当(2*max<=s[x])时,可以保证任意相邻两个都是来自不同子树,则(ans+=s[x]-1)

    否则,(ans+=s[x]-1-(max-(s[x]-max)-1)=2s[x]-2max),统计一下即可。

    为了方便,我们把(x)上的权值扔到单独建的一个点(x'),这个点的父亲是(x),这样可以把它当成子树看。

    带修改:
    考虑轻重链剖分,每个点的(son[x])是唯一(也可能没有)的那个(2s[y]>s[x])(y)

    此时,任何点到根经过的轻链数量依然满足(le logsum a[i])

    那么每次修改时,因为每次都是加,所以只会把轻链修改成重链。

    用lct维护重链,每次找到轻链,稍微讨论并对全局变量(ans)进行修改。

    时间复杂度(O(n~log~n~logsum a[i]))

    Code:

    #include<bits/stdc++.h>
    #define fo(i, x, y) for(int i = x, _b = y; i <= _b; i ++)
    #define ff(i, x, y) for(int i = x, _b = y; i <  _b; i ++)
    #define fd(i, x, y) for(int i = x, _b = y; i >= _b; i --)
    #define ll long long
    #define pp printf
    #define hh pp("
    ")
    using namespace std;
    
    const int N = 8e5 + 5;
    
    int n, m, x, y;
    
    #define V vector<int>
    #define pb push_back
    #define si size()
    
    V e[N]; int fq[N];
    void dg(int x) {
    	ff(_y, 0, e[x].si) {
    		int y = e[x][_y]; if(y == fq[x]) continue;
    		fq[y] = x;
    		dg(y);
    	}
    }
    
    ll a[N], s[N], lz[N];
    
    #define x0 t[x][0]
    #define x1 t[x][1]
    int t[N][2], fa[N], pf[N];
    void upd(int x) {
    	
    }
    void jia(int x, ll v) {
    	if(x) s[x] += v, lz[x] += v;
    }
    void down(int x) {
    	if(lz[x]) {
    		jia(x0, lz[x]);
    		jia(x1, lz[x]);
    		lz[x] = 0;
    	}	
    }
    int lr(int x) { return t[fa[x]][1] == x;} 
    void ro(int x) {
    	int y = fa[x], k = lr(x);
    	t[y][k] = t[x][!k]; if(t[x][!k]) fa[t[x][!k]] = y;
    	fa[x] = fa[y]; if(fa[y]) t[fa[y]][lr(y)] = x;
    	fa[y] = x; t[x][!k] = y; pf[x] = pf[y];
    	upd(y); upd(x);
    }
    int dd[N];
    void xc(int x) {
    	for(; x; x = fa[x]) dd[++ dd[0]] = x;
    	while(dd[0]) down(dd[dd[0] --]);
    }
    void sp(int x, int y) {
    	xc(x);
    	for(; fa[x] != y; ro(x)) if(fa[fa[x]] != y)
    		ro(lr(x) == lr(fa[x]) ? fa[x] : x);
    }
    int fl(int x) { return x0 ? fl(x0) : x;} 
    
    ll ans;
    
    void add(int x, ll v) {
    	for(int y = 0; x; )	{
    		sp(x, 0); down(x);
    		if(x > n) {
    			if(!x1) {
    				ans += v;
    			} else {
    				int z = fl(x1); sp(z, x);
    				if(2 * s[x1] <= s[x] + v) {
    					ans -= 2 * s[x] - 2 * s[x1];
    					ans += s[x] + v - 1;
    					fa[x1] = 0, pf[x1] = x; x1 = 0;
    				} else {
    					ans += 2 * v;
    				}
    			}
    		}
    		jia(x0, v); s[x] += v;
    		if(y) {
    			if(2 * s[y] > s[x]) {
    				ans -= s[x] - 1;
    				ans += 2 * s[x] - 2 * s[y];
    				x1 = y; fa[y] = x; pf[y] = 0;
    			}
    		}
    		x = fl(x), sp(x, 0);
    		upd(x), y = x, x = pf[x];
    	}
    }
    
    int main() {
    	scanf("%d %d", &n, &m);
    	fo(i, 1, n) scanf("%lld", &a[i]);
    	fo(i, 1, n - 1) {
    		scanf("%d %d", &x, &y);
    		e[x].pb(y); e[y].pb(x);
    	}
    	dg(1);
    	ans = -n;
    	fo(i, 2, n) pf[i + n] = fq[i] + n;
    	fo(i, 1, n) pf[i] = i + n;
    	fo(i, 1, n) add(i, a[i]);
    	pp("%lld
    ", ans);
    	fo(ii, 1, m) {
    		scanf("%d %d", &x, &y);
    		add(x, y);
    		pp("%lld
    ", ans);
    	}
    } 
    
  • 相关阅读:
    Java之Chat历程
    Java之静态方法中的内部类
    Java异常捕获之finally
    C语言复杂声明的本质与局限
    使用beyond compare或kompare作为git的对比、合并工具
    [二分] [洛谷] P1258 小车问题
    [STL] [洛谷] P1165 日志分析
    [洛谷] P2802 回家
    卡特兰数的应用
    [洛谷] P1722 矩阵Ⅱ
  • 原文地址:https://www.cnblogs.com/coldchair/p/12682381.html
Copyright © 2020-2023  润新知