( 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;
}