• 【luogu P4338】【LOJ 2434】历史(LCT)


    历史

    题目链接:luogu P4338 / LOJ 2434

    题目大意

    给你一棵树,其中 1 是根,然后给你每个点的 access 次数,要你规划一个 access 的顺序,使得轻重链的切换次数最大。
    然后要支持修改,修改会增加一个点的 access 次数。

    思路

    考虑不修改的时候怎么做。

    考虑 DP 求一个子树的最大切换次数。
    那除了内部贡献,还有最上面的点,那这个点最好是轮流来,除了第一次都有一个贡献,即 (sum_i-1)。((sum_i) 是子树大小)

    那怎么时候会不能有这种贡献,就是当最大的那个子树个数超过了一半,具体来讲是 (2*maxn>sum_i)
    那这个时候,你只能尽可能的交错,所以贡献只有 (2*(sum_i-maxn))

    然后你再考虑带修改。
    那带修改我们如果只有一种贡献方法就好办,但有两种的话就考虑能不能区分开。

    而且通过观察我们会发现,因为是加次数,所以它作为最大值的话就只会从第一种到第二种,如果不是最大值可能会让之前的最大值由第二种变回第一种。
    那你考虑用 LCT 的实边虚边维护,就只需要枚举到 (1) 路径上的虚边来查看是否要改动。

    那这个我们可以变形一下 access 操作来得到。
    然后你每次就查看是否要让实边取消,再看是否要让虚边连上,然后维护一些值就可以了。

    代码

    #include<cstdio>
    #define ll long long
    
    using namespace std;
    
    struct node {
    	int to, nxt;
    }e[800001];
    int n, m;
    int le[400001], KK, x, y;
    ll a[400001], ans;
    
    int re; char c;
    
    int read() {
    	re = 0; c = getchar();
    	while (c < '0' || c > '9') c = getchar();
    	while (c >= '0' && c <= '9') {
    		re = (re << 3) + (re << 1) + c - '0';
    		c = getchar();
    	}
    	return re;
    }
    
    int write(ll x) {
    	if (x > 9ll) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    struct LCT {
    	int ls[400001], rs[400001];
    	int fa[400001];
    	ll sum[400001], val[400001], xs[400001];
    	//sum 是子树的匹配个数,val 相当于 a,xs 是虚边连着的大小
    	
    	void work(int now) {//建出一开始的
    		ll maxn = val[now]; int pl = 0;
    		sum[now] = val[now];
    		for (int i = le[now]; i; i = e[i].nxt)
    			if (e[i].to != fa[now]) {
    				fa[e[i].to] = now;
    				work(e[i].to);
    				if (sum[e[i].to] > maxn) maxn = sum[e[i].to], pl = e[i].to;
    				sum[now] += sum[e[i].to];
    			}
    		if (2ll * maxn > sum[now]) ans += 2ll * (sum[now] - maxn), rs[now] = pl;
    			else ans += sum[now] - 1;
    		xs[now] = sum[now] - val[now] - sum[rs[now]];
    	}
    	
    	bool nrt(int now) {
    		return ls[fa[now]] == now || rs[fa[now]] == now;
    	}
    	
    	bool lrs(int now) {
    		return ls[fa[now]] == now;
    	}
    	
    	void up(int now) {
    		sum[now] = sum[ls[now]] + sum[rs[now]] + val[now] + xs[now];
    	}
    	
    	void rotate(int x) {
    		int y = fa[x], z = fa[y];
    		int b = lrs(x) ? rs[x] : ls[x];
    		if (z && nrt(y)) (lrs(y) ? ls[z] : rs[z]) = x;
    		if (lrs(x)) rs[x] = y, ls[y] = b;
    			else ls[x] = y, rs[y] = b;
    		fa[x] = z; fa[y] = x;
    		if (b) fa[b] = y;
    		up(y);
    	}
    	
    	void Splay(int x) {
    		while (nrt(x)) {
    			if (nrt(fa[x])) {
    				if (lrs(x) == lrs(fa[x])) rotate(fa[x]);
    					else rotate(x);
    			}
    			rotate(x);
    		}
    		up(x);
    	}
    	
    	ll clac(int now, ll summ, ll maxn) {
    		if (rs[now]) return 2ll * (summ - maxn);
    		if (val[now] * 2ll > summ) return 2ll * (summ - val[now]);
    		return summ - 1; 
    	}
    	
    	void access(int now, ll y) {
    		Splay(now);
    		
    		//一开始的那一段要特殊处理,因为是加 val 不是 xs
    		ll summ = sum[now] - sum[ls[now]], maxn = sum[rs[now]];
    		ans -= clac(now, summ, maxn);
    		sum[now] += y; val[now] += y;
    		summ += y;
    		if (2ll * maxn <= summ) xs[now] += maxn, rs[now] = 0, maxn = 0;
    		ans += clac(now, summ, maxn);
    		up(now);
    		
    		int lst = now; now = fa[now];
    		
    		for (; now; now = fa[now]) {
    			Splay(now);
    			
    			summ = sum[now] - sum[ls[now]]; maxn = sum[rs[now]];
    			ans -= clac(now, summ, maxn);
    			sum[now] += y; xs[now] += y;
    			summ += y;
    			if (2ll * maxn <= summ) xs[now] += maxn, rs[now] = 0, maxn = 0;
    			if (2ll * sum[lst] > summ) xs[now] -= sum[lst], rs[now] = lst, maxn = sum[lst];
    			ans += clac(now, summ, maxn);
    			up(now);
    			
    			lst = now;
    		}
    	}
    }T;
    
    void add(int x, int y) {
    	e[++KK] = (node){y, le[x]}; le[x] = KK;
    	e[++KK] = (node){x, le[y]}; le[y] = KK;
    }
    
    int main() {
    //	freopen("read.txt", "r", stdin);
    //	freopen("write.txt", "w", stdout);
    	
    	n = read(); m = read();
    	for (int i = 1; i <= n; i++) a[i] = 1ll * read(), T.val[i] = a[i];
    	for (int i = 1; i < n; i++) {
    		x = read(); y = read();
    		add(x, y);
    	}
    	
    	T.work(1);
    	
    	write(ans);
    	putchar('
    ');
    	while (m--) {
    		x = read(); y = read();
    		a[x] += y;
    		T.access(x, y);
    		write(ans);
    		putchar('
    ');
    	} 
    	
    	return 0;
    }
    
  • 相关阅读:
    日期时间插件
    QQ在线客服
    dede轮播图
    Animation 案例解释
    transition Css3过度详解
    解决文字无法缩小的问题
    DEDE函数
    hdu 3435 图回路分割
    HDU 4183
    hdu 1569 最小割
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/luogu_P4338.html
Copyright © 2020-2023  润新知