Description
给定一个点,初始权值 ( ext{val}=1),有两种操作:
-
可以将 ( ext{val}) 变为自己的整数倍;
-
当 ( ext{val}=w_i) 时,可以将 ( ext{val}) 变为 (e_i) ((iin[1,n]))。
问有没有哪种操作可以重复进行。
Solution
既然是权值的变化关系,也就是说一种权值只有一个点。
所以我们可以建一张图,以权值为点,以题目中给出的信息为条件建单向边,然后我们对这张图进行缩点。
如果有某个 (w_i) 和 (e_i) 在同一个 SCC 中,则证明他们互相可达或者相等,也就是说这个 (w_i o e_i) 的操作可以执行多遍。
然后这个题目就可以解决了。
当然我们还可以对其进行优化,对于每个点 (i),我们只连 ((i,i imes j),j in ext{prime}) 的边。
因为对于 ((i,i imes j),j otin ext{prime}) 的边,我们即使不连,(i) 点也可以通过其他的几条边到达 (i imes j) 点。
所以我们按其给的数据范围 (1le nle 100,000) 筛出这里的所有质数,然后连边求即可。实测跑得飞快。
Code
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define rr register
#define maxn 100010
#define INF 0x3f3f3f3f
//#define int long long
using namespace std;
bool vis[maxn],flag[maxn];
int T,t,n,tot,top,cnt,all,ans;
int W[maxn],E[maxn],head[maxn],Plist[maxn];
int low[maxn],dfn[maxn],num[maxn],zhan[maxn];
struct edge{int fr,to,nxt;}e[maxn*50];
inline int read(){
rr int s=0,w=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9')s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
inline void add(int fr,int to){
e[++tot]=(edge){fr,to,head[fr]};head[fr]=tot;
}
inline void clear(){
tot=cnt=ans=top=0;
memset(dfn,0,sizeof dfn);
memset(head,0,sizeof head);
}
void shai(){
flag[0]=flag[1]=true;
for(rr int i=2;i<=100000;i++){
if(!flag[i])Plist[++all]=i;
for(rr int j=1;j<=all&&i*Plist[j]<=100000;j++){
int x=i*Plist[j];flag[x]=true;if(!(i%Plist[j]))break;
}
}
}
void tarjan(int u){
dfn[u]=low[u]=++cnt;
zhan[++top]=u;vis[u]=1;
for(rr int i=head[u];i;i=e[i].nxt){
int to=e[i].to;
if(!dfn[to])tarjan(to),low[u]=min(low[u],low[to]);
else if(vis[to]) low[u]=min(low[u],dfn[to]);
}
if(dfn[u]==low[u]){
int pre=zhan[top--];
num[pre]=++t;vis[pre]=0;
while(pre!=u){
pre=zhan[top--];
num[pre]=t;vis[pre]=0;
}
}
}
int main(){
shai();
T=read();
while(T--){
n=read();clear();
for(rr int i=1;i<=n;i++)W[i]=read();
for(rr int i=1;i<=n;i++)add(W[i],E[i]=read());
for(rr int i=1;i<=100000;i++)
for(rr int j=1;Plist[j]*i<=100000;j++)
add(i,i*Plist[j]);
tarjan(1);
for(rr int i=1;i<=n;i++)ans+=(num[W[i]]==num[E[i]]);
printf("%d
",ans);
}
return 0;
}