suxxsfe

一言(ヒトコト)

P7520 [省选联考 2021 A 卷] 支配

https://www.luogu.com.cn/problem/P7520

考虑支配树,一个显然的结论是如果一个点的受支配集合发生改变,那么他在支配树上子树中的所有点的受支配集合都会发生改变
那么只要求出所有他的父亲不改变、他改变的点就可以了,然后往子树里推标记统计最终的标记个数即可

那么原本是要对每个询问 \((x,y)\) 找路径 \(1\rightarrow x \rightarrow y\rightarrow u\) 使得不经过 \(u\) 在支配树上的祖先,现在只要不经过他的父亲也就是 \(idom(u)\) 即可
那么对于 \(u\) 的判断就是:\(idom(u)\) 不是 \(x\) 在支配树上的祖先;\(v\) 可以在不经过 \(idom(u)\) 的情况下到达 \(u\)(在原图上)
第一个条件直接每次询问的时候判断,第二个预处理出 \(u\) 在不经过 \(idom(u)\) 情况下,从反图上走,能走到哪些点

#define N 3006
#define M 6006
struct Graph{
	int fir[N],nex[M],to[M],tot;
	inline void add(int u,int v){to[++tot]=v;nex[tot]=fir[u];fir[u]=tot;}
}G,T,H;
int dfscnt,dfn[N],id[N],fa[N];
int idom[N],sdom[N];
struct UnionSet{
	int fa[N],min[N];
	inline int find(int k){
		if(fa[k]==k) return k;
		int father=find(fa[k]);
		if(dfn[sdom[min[fa[k]]]]<dfn[sdom[min[k]]]) min[k]=min[fa[k]];
		return fa[k]=father;
	}
}S;
void dfs(int u){
	dfn[u]=++dfscnt;id[dfscnt]=u;
	for(int i=G.fir[u];i;i=G.nex[i])if(!dfn[G.to[i]]) fa[G.to[i]]=u,dfs(G.to[i]);
}
inline void build(int root){
	dfs(root);
	for(int i=1;i<=dfscnt;i++) sdom[i]=S.fa[i]=S.min[i]=i;
	for(int u,i=dfscnt;i>1;i--){
		u=id[i];
		for(int v,j=T.fir[u];j;j=T.nex[j])if(dfn[T.to[j]]){
			v=T.to[j];S.find(v);
			if(dfn[sdom[S.min[v]]]<dfn[sdom[u]]) sdom[u]=sdom[S.min[v]];
		}
		S.fa[u]=fa[u];
		H.add(sdom[u],u);u=fa[u];
		for(int v,j=H.fir[u];j;j=H.nex[j]){
			v=H.to[j];S.find(v);
			idom[v]=(u==sdom[S.min[v]])?u:S.min[v];
		}
		H.fir[u]=0;
	}
	for(int u,i=2;i<=dfscnt;i++){
		u=id[i];
		if(idom[u]^sdom[u]) idom[u]=idom[idom[u]];
	}
}
int vis[N],tim;
void dfs(int u,const Graph &G){
	vis[u]=tim;
	for(int i=G.fir[u];i;i=G.nex[i])if(vis[G.to[i]]^tim) dfs(G.to[i],G);
}
int reach[N][N],reachBan[N][N];
int changed[N],tag[N];
int main(){
	int n=read(),m=read(),q=read();
	for(int u,v,i=1;i<=m;i++){
		u=read();v=read();
		G.add(u,v);T.add(v,u);
	}
	build(1);
	for(int i=1;i<=n;i++){
		tim++;vis[idom[i]]=tim;dfs(i,T);
		for(int j=1;j<=n;j++)if(vis[j]==tim&&j!=idom[i]) reachBan[i][j]=1;
	}
	for(int i=1;i<=n;i++){
		tim++;dfs(i,G);
		for(int j=1;j<=n;j++)if(vis[j]==tim) reach[i][j]=1;
	}
	while(q--){
		int x=read(),y=read();
		for(int o=x;o;o=idom[o]) tag[o]=1;
		for(int i=2;i<=n;i++)if(!tag[idom[i]]&&reachBan[i][y]) changed[i]=1;
//			for(int i=2;i<=n;i++)if((reach[x][i]||reach[y][i])&&reachBan[i][y]&&x!=idom[i]&&idom[i]!=1) printf(">>> change: %d\n",i);
		for(int i=2;i<=dfscnt;i++) changed[id[i]]|=changed[idom[id[i]]];
		int ans=0;
		for(int i=1;i<=n;i++) ans+=changed[i];
		printEN(ans);
		__builtin_memset(changed,0,sizeof changed);__builtin_memset(tag,0,sizeof tag);
	}
	return RET;
};
posted @ 2022-03-24 19:31  suxxsfe  阅读(46)  评论(0编辑  收藏  举报