【luogu P4899】werewolf 狼人(最小生成树)(主席树)

werewolf 狼人

题目链接:luogu P4899

题目大意

给你一个无向图,然后每次要从一个地方走到另外一个地方。
然后你在走的过程中要在一个点转换形态,转换之前你只能走大于等于 L 的点,转换之后你只能走小于等于 R 的点。
然后问你对于每次询问,要你回答能不能走。

思路

首先我们考虑把要走的路分成两段。
在转换之前走的,在转化之后走的。

那我们考虑这么搞,求出转换之前能到的,求出转化之后能从这个地方到终点的(由于是双向边,所以也是从终点能到的)。
然后如果这两个点集有交的话,我们就可以到。

那接着看如何搞,首先不难看出两段其实是同样的道理,你反过来一下就好了。
那就只看转换之前能到的。
那看到只能走大于等于的,那我们考虑这么搞。

用最小生成树的方法,从大到小枚举点,再枚举另一边比它大的边,然后建树,而且让小的作为父亲。
然后你就会发现, x x x 子树内的点,就是你保证走的点大于等于 x x x,而且在 x x x 出发能到的点。

然后你考虑在这道题里面,那你从 x x x 出发,然后要大于等于 l l l,那你就可以不断的从 x x x 往上跳,跳到不能跳,也就是如果跳了跳到的位置就 < l <l <l。然后再从那个位置的子树,就是你可以到的点了。

然后问题就变成两个树的两个子树,两个数的点有对应关系,问你这两个子树里面是否有一对对应的点。

那子树嘛,自然就想到是 dfs 序上的一段区间。

那我们可以搞个二维网格,如果一对是 ( x , y ) (x,y) (x,y),那这个位置就是 1 1 1。
然后假设你两个数的两个 dfs 序上区间分别是 [ x 1 , y 1 ] , [ x 2 , y 2 ] [x1,y1],[x2,y2] [x1,y1],[x2,y2],那就是求这个矩阵里面有没有 1 1 1。

然后发现每个行或列只会有一个 1 1 1,所以我们考虑用主席树来搞。
然后就好啦。

代码

#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 200001;
const int M = 400001;
const int Q = 200001;

struct node {
	int to, nxt;
}e[M << 1];
int n, m, q, x, y, le[N], KK, l, r;

struct ababtree {
	node ee[N];
	int lee[N], KKK, fa[N][21], fath[N];
	int dfn[N], tmp, st[N], ed[N], deg[N];
	
	void clean() {
		for (int i = 1; i <= n; i++) fath[i] = i;
	}
	
	int find(int now) {
		if (fath[now] == now) return now;
		return fath[now] = find(fath[now]);
	}
	
	void add(int x, int y) {
		ee[++KKK] = (node){y, lee[x]}; lee[x] = KKK;
	}
	
	void build(bool op) {//搞最小生成树
		int kn = 0;
		if (!op) {
			for (int i = n; i >= 1; i--) {
				for (int j = le[i]; j; j = e[j].nxt)
					if (e[j].to > i) {
						int x = find(i), y = find(e[j].to);
						if (x != y) {
							fath[y] = x; add(x, y);
							kn++; if (kn == n - 1) return ;
						}
					}
			}
		}
		else {
			for (int i = 1; i <= n; i++) {
				for (int j = le[i]; j; j = e[j].nxt)
					if (e[j].to < i) {
						int x = find(i), y = find(e[j].to);
						if (x != y) {
							fath[y] = x; add(x, y);
							kn++; if (kn == n - 1) return ;
						}
					}
			}
		}
	}
	
	void dfs(int now, int father) {
		st[now] = ++tmp; dfn[tmp] = now;
		fa[now][0] = father;
		deg[now] = deg[father] + 1;
		for (int i = lee[now]; i; i = ee[i].nxt)
			dfs(ee[i].to, now);
		ed[now] = tmp;
	}
	
	void bz() {
		for (int i = 1; i <= 20; i++)
			for (int j = 1; j <= n; j++)
				fa[j][i] = fa[fa[j][i - 1]][i - 1];
	}
	
	int jmp(int x, int y, bool op) {//跳到临界点
		if (!op) {
			for (int i = 20; i >= 0; i--)
				if (fa[x][i] >= y) x = fa[x][i];
		}
		else {
			for (int i = 20; i >= 0; i--)
				if (fa[x][i] && fa[x][i] <= y) x = fa[x][i];
		}
		return x;
	}
}A, B;

struct zxtree {//主席树
	int ls[N << 6], rs[N << 6], val[N << 6], tot;
	
	int copy(int x) {
		int y = ++tot;
		ls[y] = ls[x]; rs[y] = rs[x]; val[y] = val[x];
		return y;
	}
	
	void up(int now) {
		val[now] = val[ls[now]] + val[rs[now]];
	}
	
	int insert(int now, int l, int r, int pl, int va) {
		now = copy(now);
		
		if (l == r) {
			val[now] += va;
			return now;
		}
		
		int mid = (l + r) >> 1;
		if (pl <= mid) ls[now] = insert(ls[now], l, mid, pl, va);
			else rs[now] = insert(rs[now], mid + 1, r, pl, va);
		
		up(now);
		return now;
	}
	
	int query(int now, int l, int r, int L, int R) {
		if (!now) return 0;
		if (L <= l && r <= R) return val[now];
		
		int mid = (l + r) >> 1, re = 0;
		if (L <= mid) re += query(ls[now], l, mid, L, R);
		if (mid < R) re += query(rs[now], mid + 1, r, L, R);
		return re;
	}
}T;
int rt[N];

void add(int x, int y) {
	e[++KK] = (node){y, le[x]}; le[x] = KK;
	e[++KK] = (node){x, le[y]}; le[y] = KK;
}

int main() {
	scanf("%d %d %d", &n, &m, &q);
	for (int i = 1; i <= m; i++) {
		scanf("%d %d", &x, &y); x++; y++;
		add(x, y);
	}
	
	A.clean(); B.clean();
	A.build(0); B.build(1);
	A.dfs(1, 0); B.dfs(n, 0);
	A.bz(); B.bz();
	
	for (int i = 1; i <= n; i++) {//对应的位置
		rt[i] = T.insert(rt[i - 1], 1, n, B.st[A.dfn[i]], 1);
	}
	
	while(q--) {
		scanf("%d %d %d %d", &x, &y, &l, &r);
		x++; y++; l++; r++;
		l = A.jmp(x, l, 0); r = B.jmp(y, r, 1);
		if (T.query(rt[A.ed[l]], 1, n, B.st[r], B.ed[r]) - T.query(rt[A.st[l] - 1], 1, n, B.st[r], B.ed[r])) printf("1\n");
			else printf("0\n");
	}
	
	return 0;
}
上一篇:Luogu P5395 第二类斯特林数·行


下一篇:luogu P4859 已经没有什么好害怕的了