P4899 [IOI2018] werewolf 狼人

题目描述

在日本的茨城县内共有\(N\)个城市和\(M\)条道路。这些城市是根据人口数量的升序排列的,依次编号为\(0\)\(N-1\)。每条道路连接两个不同的城市,并且可以双向通行。由这些道路,你能从任意一个城市到另外任意一个城市。

你计划了\(Q\)个行程,这些行程分别编号为\(0\)\(Q-1\)。第\(i(0 \leq i \leq Q - 1)\)个行程是从城市\(S_i\)到城市\(E_i\)​。

你是一个狼人。你有两种形态:人形和狼形。在每个行程开始的时候,你是人形。在每个行程结束的时候,你必须是狼形。在行程中,你必须要变身(从人形变成狼形)恰好一次,而且只能在某个城市内(包括可能是在\(S_i\)\(E_i\)内)变身。

狼人的生活并不容易。当你是人形时,你必须避开人少的城市,而当你是狼形时,你必须避开人多的城市。对于每一次行程\(i(0 \leq i \leq Q - 1)\),都有两个阈值 \(L_i\)\(R_i(0 \leq L_i \leq R_i \leq N - 1)\),用以表示哪些城市必须要避开。准确地说,当你是人形时,你必须避开城市\(0, 1, \ldots , L_i - 1\);而当你是狼形时,则必须避开城市\(R_i + 1, R_i + 2, \ldots , N - 1\)。这就是说,在行程\(i\)中,你必须在城市\(L_i, L_i + 1, \ldots , R_i\)中的其中一个城市内变身。

你的任务是,对每一次行程,判定是否有可能在满足上述限制的前提下,由城市\(S_i\)走到城市\(E_i\)​。你的路线可以有任意长度。

题解

首先出于个人习惯问题将城市的编号统一加\(1\)变成\([1, N]\)

对于人形相当于从\(S\)出发只能走\([L, N]\),而对于狼形相当于从\(T\)出发只能走\([1, R]\)

我们\(Kruskal\)建两棵树\(A\)\(B\)。其中\(A\)满足子节点的编号大于父节点,\(B\)满足子节点的编号小于父节点,那么我们可以在两棵树上分别从\(S\)\(T\)倍增,找到深度最小的满足两个区间限制的点,那么他的子树就都是合法的。

考虑一整棵子树的\(dfs\)序是一段区间,所以我们将点的编号的匹配转化成两段\(dfs\)序的匹配,看是否有点的\(dfs\)序分别在两棵树上满足两个区间。

这个过程我们可以使用离线树状数组类似二维数点实现,也可以用主席树以\(A\)树上的\(dfs\)序为外层下标,\(B\)树上的\(dfs\)序为内层下标实现。

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 2e5 + 5;
int n, m, Q, tot, head1[N], num, tr[N << 2], ans[N << 2];
struct node{int to, nex;}b[N << 2];
struct data{int x, y, val, id;}d[N << 2];
inline int read()
{
	int x = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') {x = (x << 3) + (x << 1) + (ch ^ 48); ch = getchar();}
	return x * f;
}
struct tree1
{
	int tot, head[N], fa[N], cnt, f[N][19], num, dfn[N], siz[N];
	struct data{int to, nex;}a[N];
	void add(int x, int y) {a[++ tot].to = y; a[tot].nex = head[x]; head[x] = tot;}
	int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
	void build()
	{
		for(int i = 1; i <= n; i ++) fa[i] = i;
		for(int i = n, x, y; i >= 1; i --) // n -- > 1
		{
			x = i;
			for(int j = head1[i]; j; j = b[j].nex)
			{
				y = b[j].to;
				if(x < y)
				{
					int xx = find(x), yy = find(y);
					if(xx == yy) continue;
					fa[yy] = xx; add(xx, yy);
					if((++ cnt) == n - 1) return;
				}
			}
		}
	}
	void dfs(int x, int fat)
	{
		f[x][0] = fat; siz[x] = 1; dfn[x] = ++ num;
		for(int i = 1; i <= 18; i ++) f[x][i] = f[f[x][i - 1]][i - 1];
		for(int i = head[x]; i; i = a[i].nex)
		{
			int y = a[i].to;
			if(y == fat) continue;
			dfs(y, x); siz[x] += siz[y];
		}
	}
	int query(int x, int em)
	{
		for(int i = 18; i >= 0; i --) if(f[x][i] && f[x][i] >= em) x = f[x][i];
		return x;
	}
}A;
struct tree2
{
	int tot, head[N], fa[N], cnt, f[N][19], num, dfn[N], siz[N];
	struct data{int to, nex;}a[N];
	void add(int x, int y) {a[++ tot].to = y; a[tot].nex = head[x]; head[x] = tot;}
	int find(int x) {return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
	void build()
	{
		for(int i = 1; i <= n; i ++) fa[i] = i;
		for(int i = 1, x, y; i <= n; i ++) // 1 -- > n
		{
			x = i;
			for(int j = head1[i]; j; j = b[j].nex)
			{
				y = b[j].to;
				if(x > y)
				{
					int xx = find(x), yy = find(y);
					if(xx == yy) continue;
					fa[yy] = xx; add(xx, yy);
					if((++ cnt) == n - 1) return;
				}
			}
		}
	}
	void dfs(int x, int fat)
	{
		f[x][0] = fat; siz[x] = 1; dfn[x] = ++ num;
		for(int i = 1; i <= 18; i ++) f[x][i] = f[f[x][i - 1]][i - 1];
		for(int i = head[x]; i; i = a[i].nex)
		{
			int y = a[i].to;
			if(y == fat) continue;
			dfs(y, x); siz[x] += siz[y];
		}
	}
	int query(int x, int em)
	{
		for(int i = 18; i >= 0; i --) if(f[x][i] && f[x][i] <= em) x = f[x][i];
		return x;
	}
}B;
void add(int x, int y) {b[++ tot].to = y; b[tot].nex = head1[x]; head1[x] = tot;}
bool cmp(const data & a, const data & b) {return a.x == b.x ? a.id < b.id : a.x < b.x;}
int lowbit(int x) {return x & (- x);}
void add(int x) {while(x <= n) {tr[x] ++; x += lowbit(x);}}
int query(int x) {int res = 0; while(x) {res += tr[x]; x -= lowbit(x);} return res;}
void work()
{
	n = read(); m = read(); Q = read();
	for(int i = 1, x, y; i <= m; i ++)
	{
		x = read() + 1; y = read() + 1;
		add(x, y); add(y, x);
	}
	A.build(); B.build(); A.dfs(1, 0); B.dfs(n, 0);
	for(int i = 1; i <= n; i ++) d[++ num] = data{A.dfn[i], B.dfn[i], 0, 0};
	for(int i = 1, s, t, l, r; i <= Q; i ++)
	{
		s = read() + 1; t = read() + 1; l = read() + 1; r = read() + 1;
		s = A.query(s, l); t = B.query(t, r);
		int x = A.dfn[s] - 1, y = A.dfn[s] + A.siz[s] - 1;
		int xx = B.dfn[t] - 1, yy = B.dfn[t] + B.siz[t] - 1;
		d[++ num] = data{x, xx, 1, i}; d[++ num] = data{x, yy, -1, i};
		d[++ num] = data{y, xx, -1, i}; d[++ num] = data{y, yy, 1, i};//
	}
	sort(d + 1, d + num + 1, cmp);
	for(int i = 1; i <= num; i ++) if(d[i].val == 0) add(d[i].y); else ans[d[i].id] += d[i].val * query(d[i].y);
	for(int i = 1; i <= Q; i ++) printf("%d\n", ans[i] == 0 ? 0 : 1);
}
int main() {return work(), 0;}
posted @ 2020-03-27 16:16  Sunny_r  阅读(173)  评论(1编辑  收藏  举报