[IOI2018] werewolf 狼人


\(\text{Problem}\)

大概就是给张图 \(n \le 200000,m \le 400000\),点权为编号本身
\(q\) 个询问,能否在 \(S\)\(E\) 的路径上找到一个点 \(X\) 使得 \(S\)\(X\) 所有点权大于等于 \(L\)\(X\)\(E\) 所有点权小于等于 \(R\)

\(\text{Solution}\)

不难想到对于 \(L\) 的限制弄一棵边权为相邻点的最小权值的最大生成树来,对 \(R\) 则是最小生成树
然后考虑在这条路径上 \(S\) 出发最远能走多远,\(E\) 出发最远能走多远,两者有交集则可行
因为两棵生成树形态显然不一样,而且很多判交集
考虑 \(Kruskal\) 重构树的性质
那么它走最远到的节点的子树中的叶子节点就是它在原图中能走过的点
询问变为判断在第一棵 \(Kruskal\) 重构树中覆盖的点和第二棵覆盖的点有没有交集
考虑两棵树各自的 \(dfs\) 序,给原图中的点记下
然后变成二维偏序问题
在线主席树,离线树状数组即可

传统题形式

\(\text{Code}\)

#include 
#include 
#include 
#define RE register
#define IN inline
using namespace std;

const int N = 4e5 + 5;
int n, m, q, Up, cnt, ans[N];
struct node{int x, y;}E[N], a[N];
struct Que{int s, t, l, r, f;}Q[N], b[N];
IN bool cmpQue(Que a, Que b){return (a.t < b.t) ? 1 : (a.t == b.t ? a.f > b.f : 0);}
struct nod{int x, y, z;}p[N];
IN bool cmpMin(nod a, nod b){return a.z < b.z;}
IN bool cmpMax(nod a, nod b){return a.z > b.z;}

IN void read(int &x)
{
	x = 0; char ch = getchar();
	for(; !isdigit(ch); ch = getchar());
	for(; isdigit(ch); x = (x<<3)+(x<<1)+(ch^48), ch = getchar());
}

struct UnionSet{
	int fa[N];
	IN void Set(){for(RE int i = 1; i <= n * 2; i++) fa[i] = i;}
	int Find(int x){return (fa[x] == x ? x : fa[x] = Find(fa[x]));}
}S;

struct KruskalTree{
	int h[N], tot, dfn[N], dfc, siz[N], f[N][20], val[N], Tot;
	struct edge{int to, nxt;}e[N << 1];
	IN void add(int x, int y){e[++tot] = edge{y, h[x]}, h[x] = tot;}
	IN void Kruskal(int ty)
	{
		for(RE int i = 1; i <= m; i++)
		{
			p[i].x = E[i].x, p[i].y = E[i].y;
			if (ty == 1) p[i].z = min(p[i].x, p[i].y);
			else p[i].z = max(p[i].x, p[i].y);
		}
		if (ty == 1) sort(p + 1, p + m + 1, cmpMax);
		else sort(p + 1, p + m + 1, cmpMin);
		S.Set(); int s = 0;
		for(RE int i = 1; i <= n; i++) val[++Tot] = i;
		for(RE int i = 1, x, y; i <= m; i++)
		{
			if ((x = S.Find(p[i].x)) == (y = S.Find(p[i].y))) continue;
			val[++Tot] = p[i].z, add(Tot, x), add(Tot, y), S.fa[x] = S.fa[y] = Tot, ++s;
			if (s == n - 1) break;
		}
	}
	IN void Dfs(int x)
	{
		for(RE int i = 1; i <= 19; i++)
			if (f[x][i - 1]) f[x][i] = f[f[x][i - 1]][i - 1];
			else break;
		dfn[x] = ++dfc, siz[x] = 1;
		for(RE int i = h[x]; i; i = e[i].nxt)
			f[e[i].to][0] = x, Dfs(e[i].to), siz[x] += siz[e[i].to];
	}
	IN void TagDFN(int ty)
	{
		Dfs(Tot), Up = max(Up, dfc);
		for(RE int i = 1; i <= n; i++)
			if (ty == 1) a[i].x = dfn[i]; else a[i].y = dfn[i];
	}
	IN int Find(int ty, int x, int v)
	{
		for(RE int i = 19; i >= 0; i--)
		if (f[x][i])
			if (ty == 1 && val[f[x][i]] >= v) x = f[x][i];
			else if (ty == 2 && val[f[x][i]] <= v) x = f[x][i];
		return x;
	}
}T1, T2;

struct BIT{
	int c[N];
	IN int lowbit(int x){return x & (-x);}
	IN void Add(int x){for(; x <= Up; x += lowbit(x)) ++c[x];}
	IN int Query(int x){int s = 0; for(; x; x -= lowbit(x)) s += c[x]; return s;}
}T;

int main()
{
	read(n), read(m), read(q);
	for(RE int i = 1; i <= m; i++) read(E[i].x), read(E[i].y), ++E[i].x, ++E[i].y;
	T1.Kruskal(1), T1.TagDFN(1), T2.Kruskal(2), T2.TagDFN(2);
	for(RE int i = 1; i <= n; i++) b[++cnt] = Que{0, a[i].x, a[i].y, 0, 2};
	for(RE int i = 1; i <= q; i++) read(Q[i].s), read(Q[i].t), read(Q[i].l), read(Q[i].r);
	for(RE int i = 1, x, y; i <= q; i++)
	{
		++Q[i].s, ++Q[i].t, ++Q[i].l, ++Q[i].r;
		x = T1.Find(1, Q[i].s, Q[i].l), y = T2.Find(2, Q[i].t, Q[i].r);
		b[++cnt] = Que{i, T1.dfn[x] - 1, T2.dfn[y], T2.dfn[y] + T2.siz[y] - 1, -1};
		b[++cnt] = Que{i, T1.dfn[x] + T1.siz[x] - 1, T2.dfn[y], T2.dfn[y] + T2.siz[y] - 1, 1};
	}
	sort(b + 1, b + cnt + 1, cmpQue);
	for(RE int i = 1; i <= cnt; i++)
		if (b[i].f == 2) T.Add(b[i].l);
		else ans[b[i].s] += (T.Query(b[i].r) - T.Query(b[i].l - 1)) * b[i].f;
	for(RE int i = 1; i <= q; i++) printf("%d\n", (ans[i] > 0));
}