【洛谷P4899】werewolf 狼人

题目

题目链接:https://www.luogu.com.cn/problem/P4899
在日本的茨城县内共有 (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)。你的路线可以有任意长度。

思路

对于一组询问,我们求出在人形和狼形的时候分别可以到达的点集,然后判断一下有无交即可。
我们设边权为连接的两个点的较小点权,求出其 kruskal 生成树,那么对于一次询问,在狼形的时候可以走的点集就是其中一个点为根的子树。同理可以设边权为较大点权,其 kruskal 生成树中的一个子树为在人形时可以走的点集。
求出两棵 kruskal 生成树之后,对于一组询问 (s,t,l,r),我们可以通过倍增找出 (s)(t) 分别在两棵树中的一个祖先节点,满足这个祖先节点的权值在可以走的范围内且深度较小。那么这两个节点的子树就是可以走的点权。
求出两棵树的 dfs 序,我们需要判断的就是两个 dfs 序的两个区间是否有交。我们以其中一棵树的 dfs 序建立可持久化线段树,以另一棵树的 dfs 序作为下标。询问时就直接判断第一个区间的可持久化线段树内,值域在第二个区间的和是否为 (0) 即可。
时间复杂度 (O((m+Q)log n))

代码

#include <bits/stdc++.h>
using namespace std;

const int N=400010,LG=20;
int n,m,Q,rt[N];

struct edge1
{
	int u,v;
}e1[N];

bool cmp1(edge1 x,edge1 y)
{
	return max(x.u,x.v)<max(y.u,y.v);
}

bool cmp2(edge1 x,edge1 y)
{
	return min(x.u,x.v)>min(y.u,y.v);
}

struct Kruscal
{
	int tot,father[N],val[N],head[N],rk[N],dfn[N],size[N],dep[N],f[N][LG+1];
	
	struct edge2
	{
		int next,to;
	}e[N];
	
	void add(int from,int to)
	{
		e[++tot]=(edge2){head[from],to};
		head[from]=tot;
	}
	
	int find(int x)
	{
		return x==father[x]?x:father[x]=find(father[x]);
	}
	
	void kruskal(int id)
	{
		memset(head,-1,sizeof(head));
		if (id==1) sort(e1+1,e1+1+m,cmp1);
			else sort(e1+1,e1+1+m,cmp2);
		for (int i=1;i<=n*2;i++) father[i]=i;
		int num=n;
		for (int i=1;i<=m;i++)
		{
			int x=find(e1[i].u),y=find(e1[i].v);
			if (x!=y)
			{
				num++;
				if (id==1) val[num]=max(e1[i].u,e1[i].v);
					else val[num]=min(e1[i].u,e1[i].v);
				add(num,x); add(num,y);
				father[x]=father[y]=num;
			}
		}
	}
	
	int dfs(int x,int fa)
	{
		dep[x]=dep[fa]+1; f[x][0]=fa;
		rk[x]=++tot; dfn[tot]=x; size[x]=1;
		if (!val[x]) val[x]=val[fa];
		for (int i=1;i<=LG;i++)	
			f[x][i]=f[f[x][i-1]][i-1];
		for (int i=head[x];~i;i=e[i].next)
			size[x]+=dfs(e[i].to,x);
		return size[x];
	}
	
	int binary(int x,int l,int r)
	{
		for (int i=LG;i>=0;i--)
			if (val[f[x][i]]>=l && val[f[x][i]]<=r) x=f[x][i];
		return x;
	}
}t1,t2;

struct SegTree
{
	int tot,lc[N*LG*4],rc[N*LG*4],sum[N*LG*4];
	
	int ins(int now,int l,int r,int k)
	{
		int x=++tot;
		lc[x]=lc[now]; rc[x]=rc[now]; sum[x]=sum[now]+1;
		if (l==r) return x;
		int mid=(l+r)>>1;
		if (k<=mid) lc[x]=ins(lc[now],l,mid,k);
			else rc[x]=ins(rc[now],mid+1,r,k);
		return x;
	}
	
	int query(int nowl,int nowr,int l,int r,int ql,int qr)
	{
		if (l==ql && r==qr)
			return sum[nowr]-sum[nowl];
		int mid=(l+r)>>1;
		if (qr<=mid) return query(lc[nowl],lc[nowr],l,mid,ql,qr);
		if (ql>mid) return query(rc[nowl],rc[nowr],mid+1,r,ql,qr);
		return query(lc[nowl],lc[nowr],l,mid,ql,mid)+query(rc[nowl],rc[nowr],mid+1,r,mid+1,qr);
	}
}seg;

int main()
{
	scanf("%d%d%d",&n,&m,&Q);
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&e1[i].u,&e1[i].v);
		e1[i].u++; e1[i].v++;
	}
	t1.kruskal(1); t2.kruskal(2);
	t1.tot=t2.tot=0;
	for (int i=1;i<=2*n;i++)
	{
		if (t1.find(i)==i) t1.dfs(i,0);
		if (t2.find(i)==i) t2.dfs(i,0);
	}
	for (int i=1;i<=n*2;i++)
		if (t2.dfn[i]<=n) rt[i]=seg.ins(rt[i-1],1,n*2,t1.rk[t2.dfn[i]]);
			else rt[i]=rt[i-1];
	while (Q--)
	{
		int s,t,l,r;
		scanf("%d%d%d%d",&s,&t,&l,&r);
		s=t2.binary(s+1,l+1,n); t=t1.binary(t+1,1,r+1);
		int p=seg.query(rt[t2.rk[s]-1],rt[t2.rk[s]+t2.size[s]-1],1,n*2,t1.rk[t],t1.rk[t]+t1.size[t]-1);
		printf("%d
",(bool)p);
	}
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14083280.html