题解:P8339 [AHOI2022] 钥匙

题意:很简单了,不再赘述。

做法:

首先我们注意到每种颜色之间的贡献是相互独立的,我们可以分别拉出来虚树处理。

因为每种钥匙只有 \(5\) 把,那么我们考虑一个钥匙怎么做贡献,那么肯定是从某个点到这个钥匙再向别的方向走到一个宝箱,这个事情其实类似于括号序列。

所以我们可以从每个钥匙开始搜索虚树,类似括号序列的方法去找到配对的宝箱并记录这些贡献对,对于询问,那么就是要看这条路径 \((u,v)\) 覆盖的贡献对的价值之和。

这个是经典的二维数点问题,这里讲细一点。

我们考虑讨论一下一个贡献对的形态:

  1. \((u,v)\) 中其中一个是另一个的祖先,这里假设 \(u\)\(v\) 的祖先,那么就要求这个路径的一个起点在 \(u\) 除了 \(v\) 这一侧的子树外,且终点在 \(v\) 内。假设 \(v\) 一直向上跳直到父亲是 \(u\) 跳到的节点是 \(p\),那么我们把树拍到 dfs 序上,就要求询问起点在 \([1, dfn_p - 1]\bigcup[out_p, n]\),终点在 \([dfn_v, out_v]\) 中。其中 \(dfn\) 是 dfs 序的编号,\(out\) 是节点 \(u\) 子树中 dfs 序最大的编号。

  2. 没有祖先关系,那么要求起点在 \([dfn_u, out_u]\),终点在 \([dfn_v,out_v]\) 中。

直接二维数点即可。

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2.5e6 + 5;
int n, m, t[maxn / 5], c[maxn / 5], dfn[maxn / 5], out[maxn / 5], st[maxn / 5 * 2][21], lg[maxn / 5  * 2], cnt, dep[maxn / 5];
vector<int> v[maxn / 5], e[maxn / 5], g[maxn / 5];
void dfs(int u, int fa) {
	st[++cnt][0] = u; dfn[u] = cnt;
	dep[u] = dep[fa] + 1;
	for (int i = 0; i < e[u].size(); i++) {
		int v = e[u][i];
		if(v == fa)
			continue;
		dfs(v, u);
		st[++cnt][0] = u;
	}
	out[u] = cnt;
}
int getmin(int x, int y) {
	return (dep[x] < dep[y] ? x : y);
}
void prepare() {
	lg[0] = -1;
	for (int i = 1; i <= cnt; i++)
		lg[i] = lg[i >> 1] + 1;
	for (int j = 1; (1 << j) <= cnt; j++)
		for (int i = 1; i + (1 << j) - 1 <= cnt; i++)
			st[i][j] = getmin(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
}
int lca(int x, int y) {
	x = dfn[x], y = dfn[y];
	if(x > y)
		swap(x, y);
	int k = lg[y - x + 1];
	return getmin(st[x][k], st[y - (1 << k) + 1][k]);
}
bool cmp(int x, int y) {
	return dfn[x] < dfn[y] ? 1 : 0; 
}
struct node {
	int st, ed;
} x[maxn];
int tot, nw;
void dfs_c(int u, int col, int cnt, int fa, int rt) {
//	cout << u << " " << cnt << endl;
	if(c[u] == col)
		(t[u] == 1 ? cnt++ : cnt--);
	if(!cnt) {
		x[++tot] = {rt, u};
		return ;
	}
	for (int i = 0; i < g[u].size(); i++) {
		int v = g[u][i];
		if(v == fa)
			continue;
		dfs_c(v, col, cnt, u, rt);
	}
}
void build(int col) {
	if(!v[col].size())
		return ;
	sort(v[col].begin(), v[col].end(), cmp);
	int len = v[col].size();
	for (int i = 0; i < len - 1; i++)
		v[col].push_back(lca(v[col][i], v[col][i + 1]));
	sort(v[col].begin(), v[col].end(), cmp);
	len = unique(v[col].begin(), v[col].end()) - v[col].begin();
//	cout << col << " asd" << len << endl;
	while(v[col].size() > len)
		v[col].pop_back();
	for (int i = 0; i < v[col].size() - 1; i++) {
		int x = v[col][i], y = v[col][i + 1];
		int d = lca(x, y);
		g[d].push_back(y), g[y].push_back(d);
	}
	for (int i = 0; i < len; i++)
		if(col == c[v[col][i]] && t[v[col][i]] == 1) {
	//		cout << v[col][i] << endl;
			dfs_c(v[col][i], col, 0, 0, v[col][i]);
		}
	for (int i = 0; i < v[col].size(); i++)
		g[v[col][i]].clear();
}
#define lowbit(x) (x & (-x))
struct Tree_array {
	int tr[maxn], n;
	void add(int x, int val) {
		while(x <= n)
			tr[x] += val, x += lowbit(x);
	}
	int query(int x) {
		int ans = 0;
		while(x)
			ans += tr[x], x -= lowbit(x);
		return ans;
	}
} tree;
struct node1 {
	int sy, ey, h, id;
	friend bool operator<(node1 x, node1 y) {
		return x.h < y.h;
	}
} y[maxn * 4];
int totn;
void prepare_mat() {
	for (int i = 1; i <= tot; i++) {
		int u = x[i].st, v = x[i].ed;
		int d = lca(u, v);
	//	cout << u << " " << v << " asdf" << d << endl;
		if(u == d) {
			for (int j = 0; j < e[u].size(); j++) {
				int p = e[u][j];
				if(dfn[p] <= dfn[v] && out[p] >= dfn[v] && dep[p] > dep[u]) {
					u = p;
					y[++totn] = {1, dfn[u] - 1, dfn[v], 1};
					y[++totn] = {out[u] + 1, cnt, dfn[v], 1};
					y[++totn] = {1, dfn[u] - 1, out[v] + 1, -1};
					y[++totn] = {out[u] + 1, cnt, out[v] + 1, -1};
			//		cout << dfn[u] << " " << dfn[v] << endl;
					break;
				}
			}
		}
		else if(v == d) {
			for (int j = 0; j < e[v].size(); j++) {
				int p = e[v][j];
				if(dfn[p] <= dfn[u] && out[p] >= dfn[u] && dep[p] > dep[v]) {
					v = p;
					y[++totn] = {dfn[u], out[u], 1, 1};
					y[++totn] = {dfn[u], out[u], dfn[v], -1};
					y[++totn] = {dfn[u], out[u], out[v] + 1, 1} ;
					y[++totn] = {dfn[u], out[u], cnt + 1, -1};
					break;
				}
			}
		}
		else {
			y[++totn] = {dfn[u], out[u], dfn[v], 1};
			y[++totn] = {dfn[u], out[u], out[v] + 1, -1};
		}
	}
	y[++totn] = {1, cnt, cnt + 1, 0};
	sort(y + 1, y + totn + 1);
}
struct query {
	int x, y, id;
	friend bool operator<(query x, query y) {
		return x.y < y.y;
	}
} q[maxn];
int totq, ans[maxn];
int main() {
	ios::sync_with_stdio(false);
	cin >> n >> m;
	for (int i = 1; i <= n; i++)
		cin >> t[i] >> c[i], v[c[i]].push_back(i);
	for (int i = 1; i < n; i++) {
		int x, y;
		cin >> x >> y;
		e[x].push_back(y);
		e[y].push_back(x);	
	}
	dfs(1, 0);
	prepare();
	for (int i = 1; i <= n; i++)
		build(i);
	prepare_mat();
	for (int i = 1; i <= m; i++) {
		int x, y; cin >> x >> y;
		x = dfn[x], y = dfn[y];
		q[++totq] = {x, y, i};
	}
	sort(q + 1, q + m + 1);
	tree.n = cnt;
	int pos = 1;
	for (int i = 1; i <= totn; i++) {
		while(pos <= m && q[pos].y < y[i].h) {
		//	cout << pos << " " << " " << q[pos].id << endl; 
			ans[q[pos].id] = tree.query(q[pos].x);
			pos++;
		}
		tree.add(y[i].sy, y[i].id);
		tree.add(y[i].ey + 1, -y[i].id);
	//	cout << y[i].sy << " " << y[i].ey << " " << y[i].h << " " << y[i].id << endl; 
	}
	for (int i = 1; i <= m; i++)
		cout << ans[i] << endl;
	return 0;
}
posted @ 2025-08-04 11:31  LUlululu1616  阅读(15)  评论(0)    收藏  举报