洛谷P5676

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;
}
原文地址:https://www.cnblogs.com/KnightL/p/14874485.html