• 【ybtoj高效进阶 21280】景点距离(DP)(换根)


    景点距离

    题目链接:ybtoj高效进阶 21280

    题目大意

    给你一个线段树结构的树,要你支持一下操作:
    删除一条边,或者判断当前有多少对点仍然连通。

    思路

    既然是树,我们考虑通过树来搞,而且还是线段树,就告诉我们它的深度是 (log) 级别的,可以每次枚举。

    而且!我们发现 (k) 最大 (40),可以只用维护 (1sim 40) 的答案。
    那你考虑一开始的答案是可以直接 (1600n) 的复杂度暴力求出。
    然后求的过程中自然是 DP,自然要设 (f_{i,j})(i) 的子树内有多少个点到 (i) 路径为 (j)。不过 (k) 只有 (40),所以第二维只用开到 (40)

    接着考虑修改的贡献。
    你修改了之后,你把那条跑找到,它两段为根,求出对于的 (f)了,那想上面一样匹配一下,就是减小的贡献数。
    那在下面的那个还好,它就相当于它子树的根,但上面的那个就不行。
    不过我们换根 DP 一下,移一下去,修改完之后移上来就可以了。

    代码

    #include<cstdio>
    #include<cstring>
    #define ll long long
    #define rr register
    
    using namespace std;
    
    int n, m, x, rre;
    ll a[200005][41];
    ll ans[41], answer;
    bool kl[200005];
    char op, c;
    
    int read() {
    	rre = 0;
    	c = getchar();
    	while (c < '0' || c > '9') c = getchar();
    	while (c >= '0' && c <= '9') {
    		rre = (rre << 3) + (rre << 1) + c - '0';
    		c = getchar();
    	}
    	return rre;
    }
    
    void write(ll x) {
    	if (x > 9) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    void dfs(int now) {//dfs 求出以 1 为根的
    	if (now > n) return ;
    	
    	dfs(now << 1); dfs(now << 1 | 1);
    	a[now][0] = 1;
    	for (int i = 1; i <= 40; i++)
    		a[now][i] = a[now << 1][i - 1] + a[now << 1 | 1][i - 1];
    	
    	for (int i = 1; i <= 40; i++)
    		ans[i] += a[now][i];
    	for (int i = 0; i <= 38; i++)
    		for (int j = 0; i + j <= 38; j++)
    			ans[i + j + 2] += a[now << 1][i] * a[now << 1 | 1][j];
    }
    
    void up(int now, int fr) {//更新影响
    	if (!now) return ;
    	if (kl[fr]) return ;
    	memset(a[now], 0, sizeof(a[now]));
    	a[now][0] = 1;
    	if (!kl[now << 1] && !kl[now << 1 | 1]) {
    		for (int i = 1; i <= 40; i++)
    			a[now][i] = a[now << 1][i - 1] + a[now << 1 | 1][i - 1];
    	}
    	else if (!kl[now << 1]) {
    		for (int i = 1; i <= 40; i++)
    			a[now][i] = a[now << 1][i - 1];
    	}
    	else if (!kl[now << 1 | 1]) {
    		for (int i = 1; i <= 40; i++)
    			a[now][i] = a[now << 1 | 1][i - 1];
    	}
    	up(now >> 1, now);
    }
    
    void down_rt(int now, int fr) {//换根
    	if (!now) return ;
    	if (kl[fr]) return ;
    	down_rt(now >> 1, now);
    	for (int i = 1; i <= 40; i++)
    		a[now][i] -= a[fr][i - 1];
    	for (int i = 1; i <= 40; i++)
    		a[fr][i] += a[now][i - 1];
    }
    
    void up_rt(int now, int fr) {
    	if (!now) return ;
    	if (kl[fr]) return ;
    	for (int i = 1; i <= 40; i++)
    		a[fr][i] -= a[now][i - 1];
    	for (int i = 1; i <= 40; i++)
    		a[now][i] += a[fr][i - 1];
    	up_rt(now >> 1, now);
    }
    
    int main() {
    //	freopen("dis.in", "r", stdin);
    //	freopen("dis.out", "w", stdout);
    	
    	n = read(); m = read();
    	
    	dfs(1);
    	
    	while (m--) {
    		op = getchar();
    		while (op != '-' && op != '?') op = getchar();
    		
    		x = read();
    		if (op == '?') {
    			answer = 0;
    			for (int i = 1; i <= x; i++)
    				answer += ans[i];
    			write(answer); putchar('
    ');
    		}
    		if (op == '-') {
    			if (kl[x ^ 1]) {
    				memset(a[x >> 1], 0, sizeof(a[x >> 1]));
    				a[x >> 1][0] = 1;
    			}
    			else {
    				a[x >> 1][0] = 1;
    				for (int i = 1; i <= 40; i++)
    					a[x >> 1][i] = a[x ^ 1][i - 1];
    			}
    			up(x >> 2, x >> 1);
    			
    			down_rt(x >> 2, x >> 1);
    			
    			for (int i = 0; i <= 39; i++)//减去贡献
    				for (int j = 0; i + j <= 39; j++)
    					ans[i + j + 1] -= a[x][i] * a[x >> 1][j];
    			
    			up_rt(x >> 2, x >> 1);
    			
    			kl[x] = 1;
    		}
    	}
    	
    	return 0;
    }
    
  • 相关阅读:
    python打包exe pyinstaller 简单使用
    加群请回答两个问题,谢谢合作!
    8080_32位汇编-通用寄存器
    学习8086汇编的准备
    HelloWorld生命周期
    我为什么要学习《深入理解计算机系统》这本书?
    MySQL_1.0
    C++命名空间的玩法
    Linux的 7个运行级别
    Linux基础命令2.0
  • 原文地址:https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21280.html
Copyright © 2020-2023  润新知