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

景点距离

题目链接:ybtoj高效进阶 21280

题目大意

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

思路

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

而且!我们发现 k 最大 40,可以只用维护 140 的答案。
那你考虑一开始的答案是可以直接 1600n 的复杂度暴力求出。
然后求的过程中自然是 DP,自然要设 fi,ji 的子树内有多少个点到 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('\n'); } 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; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBTOJ_GXJJ_21280.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(33)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示