「题解」洛谷P5311 [Ynoi2011] 成都七中

对于每一个询问,在点分树上一定存在一个最浅的点,满足这个点是 \(x\) 在点分树中的祖先,且在点分树上到 \(x\) 经过的点编号最小/大值组成的区间 \([\min,\max]\) 被 \([l,r]\) 包含,那么所有编号在 \([l,r]\) 内的与 \(x\) 连通的点,一定都在点分树中这个最浅的点的子树内,因为如果存在另一个点不在这个子树内且与 \(x\) 连通,则这个不在子树内的点和我们找到的最浅的点之间一定存在一个路径,经过的编号都是合法的。所以他们的 LCA 也是合法的,既然 LCA 合法并且更浅,与我们“找到的最浅的点”相矛盾。

所以把每个询问离线下来,找到对应的最浅的点,让这个最浅的点处理这个询问。问题转化成了求一个(点分树中的)有根树中与 \(x\) 在原树中连通的连通块颜色数。

发现一个点满足条件当且仅当这个点到根经过的点的 \(\min\geq l,\max \leq r\),也就是经过的每一个点都满足条件。

对于每一个分治中心处理这个询问,是一个二维数颜色问题,和二维数点问题差不多一样做就行,按照 \(l\) 从后往前扫,记录每个颜色之前出现的最小的 \(r\),对于每个颜色只在最小的 \(r\) 处 \(+1\) 即可。

#include<iostream>
#include<cstdio>
#include<vector>
#include<algorithm>
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T& read(T& r) {
	r = 0; bool w = 0; char ch = getchar();
	while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
	while(ch >= '0' && ch <= '9') r = r * 10 + (ch ^ 48), ch = getchar();
	return r = w ? -r : r;
}
const int N = 100010;
inline int lowbit(int x) { return x & (-x); }
int n, m, head[N], ent, tot;
int tree[N];
void modify(int x, int v) { for(; x <= n; x += lowbit(x)) tree[x] += v; }
int query(int x) { int sumq = 0; for(; x; x -= lowbit(x)) sumq += tree[x]; return sumq; }
int mx[N], siz[N], fa[N], col[N], col_[N], lst[N], ans[N];
int all, rt, now;
bool vis[N];
struct Edge {
	int next, to;
}e[N << 1];
inline void add(int x, int y) {
	e[++ent].to = y; e[ent].next = head[x]; head[x] = ent;
}
struct Node {
	int l, r, id;
	Node(int ll = 0, int rr = 0, int idd = 0) { l = ll; r = rr; id = idd; }
};
bool cmp(Node x, Node y) { return x.l == y.l ? x.id > y.id : x.l > y.l; }
std::vector<Node>vec1[N], vec2[N];
void dfs(int x, int f, int mn, int mx) {
	vec1[x].push_back(Node(mn, mx, rt)); vec2[rt].push_back(Node(mn, mx, col[x]));
	siz[x] = 1;
	for(int i = head[x]; i; i = e[i].next) {
		int v = e[i].to; if(v == f || vis[v]) continue ;
		dfs(v, x, Min(mn, v), Max(mx, v));
		siz[x] += siz[v];
	}
}
void getrt(int x, int f) {
	mx[x] = siz[x] = 1;
	for(int i = head[x]; i; i = e[i].next) {
		int v = e[i].to; if(v == f || vis[v]) continue ;
		getrt(v, x); siz[x] += siz[v]; mx[x] = Max(mx[x], siz[v]);
	} mx[x] = Max(mx[x], all - siz[x]);
	if(!rt || mx[x] < now) rt = x, now = mx[x];
}
void work(int x) {
	all = siz[x]; rt = 0; now = 0x7fffffff;
	getrt(x, 0); vis[rt] = 1;
	dfs(x = rt, 0, rt, rt);
	for(int i = head[x]; i; i = e[i].next) {
		int v = e[i].to; if(vis[v]) continue ;
		work(v);
	}
}
signed main() { //freopen("in.txt", "r", stdin);
	read(n); read(m); 
	for(int i = 1; i <= n; ++i) col_[i] = read(col[i]);
	std::sort(col_+1, col_+n+1);
	tot = std::unique(col_+1, col_+n+1) - col_ - 1;
	for(int i = 1; i <= n; ++i) col[i] = std::lower_bound(col_+1, col_+tot+1, col[i]) - col_;
	for(int i = 1, u, v; i < n; ++i) read(u), read(v), add(u, v), add(v, u);
	siz[1] = n;
	work(1);
	for(int i = 1; i <= m; ++i) {
		int x, y, z; read(x); read(y); read(z);
		for(auto j : vec1[z]) {
			if(j.l >= x && j.r <= y) {
				vec2[j.id].push_back(Node(x, y, -i));
				break ;
			}
		}
	}
	for(int i = 1; i <= n; ++i) {
		std::sort(vec2[i].begin(), vec2[i].end(), cmp);
		for(auto j : vec2[i]) {
			if(j.id < 0)
				ans[-j.id] = query(j.r);
			else {
				if(!lst[j.id] || lst[j.id] > j.r) {
					if(lst[j.id]) modify(lst[j.id], -1);
					lst[j.id] = j.r;
					modify(lst[j.id], 1);
				}
			}
		}
		for(auto j : vec2[i])
			if(j.id > 0 && lst[j.id]) {
				modify(lst[j.id], -1);
				lst[j.id] = 0;
			}
	}
	for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
	return 0;
}
上一篇:[SDOI2008] 洞穴勘测


下一篇:完全JavaScript Web Stack-中间件,Web服务器,数据库建议?