P7394 「TOCO Round 1」History

操作树加二分,目前题解区没有这种做法。

发现操作一可逆,可以用操作树,操作三解决。

操作一单点修改没什么好说的。

接下来看操作二。令 \(fa_{x,k}\)\(x\)\(k\) 级祖先。

发现对于每个询问中,如果 \(y\) 为奇数那么答案为 \(0\)。如果 \(y\) 为偶数,那么答案就是 \(fa_{x,y/2}\)\(y/2\) 级儿子的开灯的个数减 \(fa_{x,y/2-1}\)\(y/2-1\) 级儿子的开灯的个数。

具体实现方法就是先对这棵树跑出每个点的 bfs 序和 dfs 序,记树的第 \(i\) 层最大的 bfs 序为 \(lst_i\)。所有深度为 \(d\) 的点的 bfs 序都在 \([lst_{d-1}+1,lst_d]\),在这个区间二分。

举个例子:

现在的询问 \(x=5,y=4\),那么 \(fa_{x,y/2}=2\),二分时如果二分出的点在以 \(fa_{x,y/2}\) 为根的子树内,视想要哪边的端点移动二分区间。如果不在这棵子树内,则与 \(fa_{x,y/2}\) 的 dfs 序比较。如果小,二分区间右移;如果大,二分区间左移。例如,目前二分到了 \(11\) 号节点,它的 dfs 序小于 \(2\) 的 dfs 序,那么二分区间右移。

最终答案为红色区间中的开灯个数减蓝色中的开灯个数。

用支持单点修改,区间求和的数据结构(例如树状数组)维护下就好了。

复杂度 \(O(n\log n)\)

#include <bits/stdc++.h>

using namespace std;

int read() {
    int s = 0, w = 1;
    char c = getchar();
    while (!isdigit(c)) {
        if (c == '-')
            w = -w;
        c = getchar();
    }
    while (isdigit(c)) {
        s = s * 10 + c - 48;
        c = getchar();
    }
    return s * w;
}
void pr(int x) {
    if (x < 0)
        putchar('-'), x = -x;
    if (x > 9)
        pr(x / 10);
    putchar(x % 10 + 48);
}
#define end_ putchar('\n')
#define spc_ putchar(' ')

const int maxN = 1e5 + 7;

int n, m;

vector<int> E[maxN];

int bfn[maxN], dfn[maxN], tot;
int dep[maxN], siz[maxN], lst[maxN], rk[maxN];

int fa[25][maxN];

void dfs(int x, int f) {
	dep[x] = dep[f] + 1;
	siz[x] = 1;
	dfn[x] = ++tot;
	
	fa[0][x] = f;
	for (int t = 1; t <= 20; t++)
		fa[t][x] = fa[t - 1][fa[t - 1][x]];
		
	for (int to : E[x])
		if (to != f) {
			dfs(to, x);
			siz[x] += siz[to];
		}
}
void bfs() {
	tot = 0;
	queue<int> Q;
	Q.push(1);
	while (!Q.empty()) {
		int f = Q.front();
		Q.pop();
		bfn[f] = ++tot;
		rk[tot] = f;
		for (int to : E[f])
			if (dep[to] > dep[f])
				Q.push(to);
	}
}

int find(int x, int k) {
	for (int i = 0; k; i++, k >>= 1)
		if (k & 1)
			x = fa[i][x];
	return x;
}

int cnt;
struct node {
	int to, x, y, id;
	node(int a, int b) {
		to = a, x = b;
		y = id = -1;
	}
	node(int a, int b, int c, int d) {
		to = a, x = b, y = c, id = d;
	}
};
vector<node> mo[maxN];

int qcnt;
struct ques {
	int x, y, id;
};
vector<ques> q[maxN];
int ans[maxN];

int v[maxN];
int t[maxN];
void add(int x, int v) {
	for (; x <= n; x += x & -x)
		t[x] += v;
}
int ask(int x) {
	int res = 0;
	for (; x > 0; x -= x & -x)
		res += t[x];
	return res;
}

int solve(int x, int y) {
	if (y == 0)
		return v[x];
	if (y & 1)
		return 0;
	y >>= 1;
	
	int f = find(x, y);
	if (!f)
		return 0;
	
	auto erfen = [x](bool ty, int f) {
		int L = lst[dep[x] - 1] + 1, R = lst[dep[x]];
		int ans = -1;
		while (L <= R) {
			int mid = (L + R) >> 1;
			int p = rk[mid];
			if (dfn[f] <= dfn[p] && dfn[p] <= dfn[f] + siz[f] - 1) {
				ans = p;
				if (ty)
					R = mid - 1;
				else
					L = mid + 1;
			}
			else if (dfn[p] < dfn[f])
				L = mid + 1;
			else
				R = mid - 1;
		}
		return ans;
	};
	
	int l = erfen(true, f), r = erfen(false, f);
	int res = ask(bfn[r]) - ask(bfn[l] - 1);
	
	f = find(x, y - 1);
	
	l = erfen(true, f), r = erfen(false, f);
	int tmp = ask(bfn[r]) - ask(bfn[l] - 1);
	
	return res - tmp;
}

void calc(int x) {
	for (auto i : mo[x]) {
		if (i.x != -1 && i.id == -1) {
			if (v[i.x])
				v[i.x] = 0, add(bfn[i.x], -1);
			else
				v[i.x] = 1, add(bfn[i.x], 1);
		}
		if (i.id != -1)
			ans[i.id] = solve(i.x, i.y);
		
		calc(i.to);
		
		if (i.x != -1 && i.id == -1) {
			if (v[i.x])
				v[i.x] = 0, add(bfn[i.x], -1);
			else
				v[i.x] = 1, add(bfn[i.x], 1);
		}
	}
}

int main() {
	n = read();
	for (int i = 1; i < n; i++) {
		int u = read(), v = read();
		E[u].push_back(v);
		E[v].push_back(u);
	}
	dfs(1, 0);
	bfs();
	for (int i = 1; i <= n; i++)
		lst[dep[i]] = max(lst[dep[i]], bfn[i]);
	
	memset(ans, 255, sizeof(ans));
	
	m = read();
	for (int i = 1; i <= m; i++) {
		int op = read();
		if (op == 1) {
			int x = read();
			mo[cnt].emplace_back(cnt + 1, x);
			++cnt;
		}
		if (op == 2) {
			int x = read(), y = read();
			mo[cnt].emplace_back(cnt + 1, x, y, i);
			++cnt;
		}
		if (op == 3) {
			int x = read();
			mo[x].emplace_back(cnt + 1, -1);
			++cnt;
		}
	}
	
	calc(0);
	
	for (int i = 1; i <= m; i++)
		if (ans[i] != -1)
			pr(ans[i]), end_;
}
posted @ 2024-10-10 09:39  ccxswl  阅读(13)  评论(1编辑  收藏  举报