题解「IOI 2018 狼人」

一句话题意:(n) 个点 (m) 条边的图,有 (Q) 个询问,每次从 (s_i) 出发,只能够到达 (geq l_i) 的点,在 ([l_i,r_i]) 必须变身一次,之后只能够到达 (leq r_i) 的点,问能否到达 (t_i)

我们发现这个问题等价于从 (s_i) 出发能够到达的点与从 (t_i) 出发能够到达的点是否存在交集。对于这一类对路径途径的点 (/) 边进行限制,却只要求判断连通性 (/) 可达性的问题,可以使用 (mathrm{Kruskal}) 重构树解决。

基于 (mathrm{Kruskal}) 重构树的连通性与原生成树相同,可以很好的处理这个问题。但是 (mathrm{Kruskal}) 重构树需要有边权,原图上没有,怎么办呢?我们给每条边 ((x,y)) 赋上一个权值 (w) 即可。

这里以 (leq r_i) 为例,重构过程中,把一条边转换成一个点,说明这两个点在生成树上必须经过这条边 ((u,v)),并且经过这条边时,只有 (max(u,v)) 可能会对当前经过点的最大值产生影响。所以将 ((u,v)) 的边权赋为 (max(u,v)),然后求最小生成树的 (mathrm{Kruskal}) 重构树,记这棵 (mathrm{Kruskal}) 重构树为 (T_1)(geq l_i) 的部分同理。参照上述分析过程,将 ((u,v)) 边权赋为 (min(u,v)) 再求最大生成树的 (mathrm{Kruskal}) 重构树,记这棵 (mathrm{Kruskal}) 重构树为 (T_2)

那么,我们现在得到了两棵重构树 (T_1,T_2),并且能够在每次询问时取出这两棵树的子树 (x,y),问题变为判断这两棵子树 (x,y) 是否存在交集。将这两棵树的 (mathrm{dfs}) 序求出来。问题再次被转化成判断两个序列是否存在交集。

记子树 (x)(T_1)(mathrm{dfs}) 序上对应区间 ([l_1,r_1]),子树 (y)(T_2)(mathrm{dfs}) 序上对应区间 ([l_2,r_2])(T_1)(mathrm{dfs}) 序列为 (a)(T_2)(mathrm{dfs}) 序列为 (b) 。将这个问题形式化,即判断 (exists iin [l_1,r_1],jin [l_2,r_2],a_i,b_jleq n,a_i=b_j)。似乎不是很好做,不妨固定其中一个约束。记 (c_x) 为节点编号 (x) 在序列 (a) 中出现的位置。则以上判定可以被写作 (exists b_jleq n,jin [l_2,r_2],c_{b_j}in [l_1,r_1])。直接主席树维护就好了。

时间复杂度为 (O((Q+n) log n+mlog m))

#include<cstdio>
#include<algorithm>
struct edge {int x,y,w;} e[2][400005];
int n,m,t,tot[2],cnt[2],num[2];
int h[2][400005],to[2][400005],ver[2][400005];
int f[2][400005][20],g[2][400005][20],fa[400005];
int dfn[2][400005],rev[2][400005],size[2][400005],val[2][400005];
int rt[400005],sonL[16000005],sonR[16000005],sum[16000005];
inline int read() {
	register int x=0,f=1;register char s=getchar();
	while(s>'9'||s<'0') {if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
	return x*f;
}
inline int find(int x) {return x==fa[x]? x:fa[x]=find(fa[x]);}
inline bool cmp(const edge &x,const edge &y) {return x.w<y.w;} 
inline int max(const int &x,const int &y) {return x>y? x:y;}
inline int min(const int &x,const int &y) {return x<y? x:y;}
inline void assign(int x,int y) {sonL[x]=sonL[y]; sonR[x]=sonR[y]; sum[x]=sum[y];}
inline void add(int x,int y,int id) {to[id][++cnt[id]]=y;ver[id][cnt[id]]=h[id][x];h[id][x]=cnt[id];}
inline void dfs1(int x,const int &id,int fa) {
	dfn[id][x]=++num[id]; rev[id][num[id]]=x; size[id][x]=1; //printf("%d
",x);
	for(register int i=1;i<=19;++i) f[id][x][i]=f[id][f[id][x][i-1]][i-1],g[id][x][i]=max(g[id][x][i-1],g[id][f[id][x][i-1]][i-1]);
	for(register int i=h[id][x],y;i;i=ver[id][i]) {if((y=to[id][i])==fa) continue; f[id][y][0]=x; g[id][y][0]=val[id][x]; dfs1(y,id,x); size[id][x]+=size[id][y];} 
}
inline int jump(int u,int lim,int id) {for(register int i=19;i>=0;--i) if(f[id][u][i]&&g[id][u][i]<=lim) u=f[id][u][i]; return u;}
inline void prework(int id) {
	std::sort(e[id]+1,e[id]+1+m,cmp); tot[id]=n; for(register int i=1;i<=2*n-1;++i) fa[i]=i;
	for(register int i=1,fx,fy;i<=m;++i) if((fx=find(e[id][i].x))!=(fy=find(e[id][i].y))) {fa[fx]=fa[fy]=++tot[id]; val[id][tot[id]]=e[id][i].w; add(tot[id],fx,id); add(tot[id],fy,id);}
	for(register int i=n+1;i<=tot[id];++i) if(fa[i]==i) dfs1(i,id,-1);
}
inline void change(int &p,int lst,int l,int r,int x) {assign(p=++t,lst); ++sum[p]; if(l==r) return; int mid=l+r>>1; x<=mid? change(sonL[p],sonL[lst],l,mid,x):change(sonR[p],sonR[lst],mid+1,r,x);}
inline int ask(int x,int y,int l,int r,int L,int R) {if(L<=l&&r<=R) return sum[y]-sum[x]; int mid=l+r>>1,res=0; if(L<=mid) res+=ask(sonL[x],sonL[y],l,mid,L,R); if(R>mid) res+=ask(sonR[x],sonR[y],mid+1,r,L,R); return res;}
int main() {
	n=read();m=read(); int Q=read();
	for(register int i=1;i<=m;++i) {
		e[0][i].x=e[1][i].x=read()+1;
		e[0][i].y=e[1][i].y=read()+1;
		e[0][i].w=max(e[0][i].x,e[0][i].y);
		e[1][i].w=-min(e[0][i].x,e[0][i].y);
	}
	prework(0); prework(1);
	for(register int i=1,x;i<=tot[1];++i) {if((x=rev[1][i])<=n) change(rt[i],rt[i-1],1,tot[0],dfn[0][x]); else rt[i]=rt[i-1];}
	while(Q--) {
		int S=read()+1,E=read()+1,L=read()+1,R=read()+1;
		int u=jump(S,-L,1),v=jump(E,R,0);
		int l1=dfn[0][v],r1=dfn[0][v]+size[0][v]-1,l2=dfn[1][u],r2=dfn[1][u]+size[1][u]-1;
		int res=ask(rt[l2-1],rt[r2],1,tot[0],l1,r1);
		printf("%d
",res>0); 
	}
	return 0; 
} 
原文地址:https://www.cnblogs.com/tommy0103/p/13831833.html