【题解】 [GZOI2017]小z玩游戏

题目戳我

( ext{Solution:})

考虑建图。操作可以看作对(1)进行的操作,于是有以下运行过程:

(1 o w[i] o e[i] o...)

考虑倍数,一个数可以走到所有是它的倍数的数。于是这样建图:

(w[i] o e[i],i o i*j.)

然后发现倍数的边可以只建立质数的倍数即可。

题目本质是将所谓游戏重玩看成环,并将操作抽象为对初始元素(1)进行的状态转移,进而建立出模型。

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
int T,n,w[MAXN],e[MAXN],head[500010],tot,id,p[MAXN];
struct Edge{int nxt,to;}edge[500010];
int dfn[MAXN],inst[MAXN],low[MAXN],vis[MAXN],col[MAXN],c;
int st[MAXN],top,cnt;
void Euler(){
	for(int i=2;i<=100000;++i){
		if(!vis[i])p[++cnt]=i;
		for(int j=1;j<=cnt&&i*p[j]<=100000;++j){
			vis[i*p[j]]=1;
			if(i%p[j]==0)break;
		}
	}
}
inline void add(int x,int y){
	edge[++tot].to=y;
	edge[tot].nxt=head[x];
	head[x]=tot;
}
void pre(){
	for(int i=0;i<=100000;++i)dfn[i]=low[i]=inst[i]=st[i]=col[i]=0;
	for(int i=1;i<=tot;++i)head[i]=0;
	tot=0,top=0,id=0,c=0;
}
void Link(){
	for(int i=1;i<=n;++i)add(w[i],e[i]);
	for(int i=1;i<=100000;++i)
		for(int j=1;i*p[j]<=100000;++j)
			add(i,i*p[j]);
}
void Tarjan(int x){
	st[++top]=x;low[x]=dfn[x]=++id;inst[x]=1;
	for(int i=head[x];i;i=edge[i].nxt){
		int j=edge[i].to;
		if(!dfn[j]){
			Tarjan(j);
			low[x]=min(low[x],low[j]);
		}
		else if(inst[j])low[x]=min(low[x],dfn[j]);
	}
	if(low[x]==dfn[x]){
		int y;++c;
		while(y=st[top--]){
			inst[y]=0;
			col[y]=c;
			vis[y]=1;
			if(x==y)break;
		}
	}
}

int main(){
	scanf("%d",&T);
	Euler();p[++cnt]=20060727;
	while(T--){
		pre();
		scanf("%d",&n);
		for(int i=1;i<=n;++i)scanf("%d",&w[i]);
		for(int i=1;i<=n;++i)scanf("%d",&e[i]);
		Link();
		Tarjan(1);
		int ans=0;
		for(int i=1;i<=n;++i)if(col[w[i]]==col[e[i]])ans++;
		printf("%d
",ans);
	}
	return 0;
} 
原文地址:https://www.cnblogs.com/h-lka/p/13762841.html