软件管理 / 软件包管理器
题目链接:ybt金牌导航5-1-2 / luogu P2146
题目大意
有一个树,你每次会操作,把一条到根节点的链上点权都改成一,或把一个点与其子树的点权都改成 0。
问你每次操作树上点权改变的点的个数。
思路
这道题看到修改链上点权,不难想到可以用树链剖分。
但是它还有修改子树啊。
但你会发现,根据重儿子先走的 dfs 序,一个子树上的点的 dfs 序还是连续的。(毕竟它还是 dfs)
那就是说你还是可以用线段树来搞,那位置就是
p
l
x
∼
p
l
x
+
s
z
x
−
1
pl_x\sim pl_x+sz_x-1
plx∼plx+szx−1。
(
p
l
x
pl_x
plx 是
x
x
x 点在线段树中的位置,
s
z
x
sz_x
szx 是
x
x
x 所在子树的大小)
然后每次就查询,然后修改就好了。
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
struct node {
int to, nxt;
}e[100001];
int n, fa[100001], le[100001], KK, tmp, Q, x;
int deg[100001], sz[100001], son[100001];
int top[100001], pl[100001], dfn[100001];
int sum[400001], lazy[400001];
char op;
void add(int x, int y) {
e[++KK] = (node){y, le[x]}; le[x] = KK;
}
void dfs1(int now, int father) {//树链剖分两次 dfs
deg[now] = deg[father] + 1;
sz[now] = 1;
int maxn = 0;
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father) {
dfs1(e[i].to, now);
sz[now] += sz[e[i].to];
if (sz[e[i].to] > maxn) {
maxn = sz[e[i].to];
son[now] = e[i].to;
}
}
}
void dfs2(int now, int father) {
if (son[now]) {
pl[son[now]] = ++tmp;
top[son[now]] = top[now];
dfn[tmp] = son[now];
dfs2(son[now], now);
}
for (int i = le[now]; i; i = e[i].nxt)
if (e[i].to != father && e[i].to != son[now]) {
pl[e[i].to] = ++tmp;
top[e[i].to] = e[i].to;
dfn[tmp] = e[i].to;
dfs2(e[i].to, now);
}
}
void down(int now, int l, int r) {//线段树操作
if (lazy[now] != -1 && l != r) {
int mid = (l + r) >> 1;
sum[now << 1] = lazy[now] * (mid - l + 1);
sum[now << 1 | 1] = lazy[now] * (r - (mid + 1) + 1);
lazy[now << 1] = lazy[now << 1 | 1] = lazy[now];
lazy[now] = -1;
}
}
void up(int now) {
sum[now] = sum[now << 1] + sum[now << 1 | 1];
}
int query(int now, int l, int r, int L, int R) {//此查询是查询 1 的个数
if (L <= l && r <= R) return sum[now];
down(now, l, r);
int mid = (l + r) >> 1, re = 0;
if (L <= mid) re += query(now << 1, l, mid, L, R);
if (mid < R) re += query(now << 1 | 1, mid + 1, r, L, R);
return re;
}
void change(int now, int l, int r, int L, int R, int num) {
if (L <= l && r <= R) {
lazy[now] = num;
sum[now] = num * (r - l + 1);
return ;
}
down(now, l, r);
int mid = (l + r) >> 1;
if (L <= mid) change(now << 1, l, mid, L, R, num);
if (mid < R) change(now << 1 | 1, mid + 1, r, L, R, num);
up(now);
}
//注意修改成一的是要查询 0 的个数,那 0 的个数就是所有的个数 - 1 的个数
int ask_and_make(int x, int y) {
int X = top[x], Y = top[y], re = 0;
while (X != Y) {
if (deg[X] < deg[Y]) {
swap(X, Y);
swap(x, y);
}
re += (deg[x] - deg[X] + 1) - query(1, 1, tmp, pl[X], pl[x]);
change(1, 1, tmp, pl[X], pl[x], 1);
x = fa[X];
X = top[x];
}
if (deg[x] > deg[y]) swap(x, y);
re += (deg[y] - deg[x] + 1) - query(1, 1, tmp, pl[x], pl[y]);
change(1, 1, tmp, pl[x], pl[y], 1);
return re;
}
int ask_and_clear(int x) {
int re = query(1, 1, tmp, pl[x], pl[x] + sz[x] - 1);
change(1, 1, tmp, pl[x], pl[x] + sz[x] - 1, 0);
return re;
}
int main() {
memset(lazy, -1, sizeof(lazy));
scanf("%d", &n);
for (int i = 1; i < n; i++) {
scanf("%d", &fa[i]);
add(fa[i], i);
}
dfs1(0, 0);
pl[0] = ++tmp; top[0] = 0; dfn[1] = 0;
dfs2(0, 0);
scanf("%d", &Q);
while (Q--) {
op = getchar();
while (op != 'i' && op != 'u') op = getchar();
if (op == 'i') {
for (int i = 1; i <= 6; i++) getchar();
scanf("%d", &x);
printf("%d\n", ask_and_make(0, x));
continue;
}
if (op == 'u') {
for (int i = 1; i <= 8; i++) getchar();
scanf("%d", &x);
printf("%d\n", ask_and_clear(x));
continue;
}
}
return 0;
}