【ybtoj高效进阶 21280】景点距离(DP)(换根)
景点距离
题目链接:ybtoj高效进阶 21280
题目大意
给你一个线段树结构的树,要你支持一下操作:
删除一条边,或者判断当前有多少对点仍然连通。
思路
既然是树,我们考虑通过树来搞,而且还是线段树,就告诉我们它的深度是 \(\log\) 级别的,可以每次枚举。
而且!我们发现 \(k\) 最大 \(40\),可以只用维护 \(1\sim 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('\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;
}