原题来自与:洛谷 P5676(GZOI2017) 链接: https://www.luogu.com.cn/problem/P5676
题面:
题意比较明显,如果已经建好了边,那么跑个Tarjan 就完了。
但是问题在于建边的复杂度,比较好想的是n2 的建边方式。
但是时间肯定不允许。
那么我们就要想一种时间复杂度较小的建边方式。
可以考虑引入中间变量兴奋程度
那么如何建边,
首先将点开多一些
把中间变量兴奋程度也当作点
然后建边
1.建一个由 有趣程度 到 点 的边
2.建一个由 点 到 兴奋程度 的边
3.重点:建一个兴奋程度整数倍的边。
然后就跑一边tarjan就完了,
下面是代码(加注释):
#include <cstdio> #include <algorithm> #include <cstring> using namespace std; const int maxn=500005*4; int n; int Max=0; struct edge{ int to,next; }e[maxn];int head[maxn],cnt=0; void add(int x,int y){ e[++cnt].to=y;e[cnt].next=head[x];head[x]=cnt; } int w[maxn],b[maxn]; int dfn[maxn],low[maxn],dfs_clock,sta[maxn],top,belong[maxn],siz[maxn],vis[maxn],dcc; void dfs(int u){ dfn[u]=low[u]=++dfs_clock; sta[++top]=u; for(int i=head[u];i;i=e[i].next){ int v=e[i].to; if(!dfn[v]){ dfs(v); low[u]=min(low[u],low[v]); } else if(!belong[v]){ low[u]=min(low[u],dfn[v]); } } if(dfn[u]==low[u]){//特殊的出栈方式 dcc++; belong[u]=dcc; siz[dcc]++; while(sta[top]!=u){ int x=sta[top--]; belong[x]=dcc; siz[dcc]++; vis[x]=1; } if(siz[dcc]>1)vis[u]=1; top--; } } void clear(){ memset(e,0,sizeof(e)); memset(head,0,sizeof(head)); cnt=0;Max=0; memset(w,0,sizeof(w)); memset(b,0,sizeof(b)); memset(dfn,0,sizeof(dfn)); memset(low,0,sizeof(low)); dfs_clock=0; memset(sta,0,sizeof(siz)); top=0; memset(belong,0,sizeof(belong)); memset(siz,0,sizeof(siz)); memset(vis,0,sizeof(vis)); dcc=0; } int main(){ //freopen("a.in","r",stdin); int t;scanf("%d",&t); while(t--){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&w[i]); add(n+w[i],i); Max=max(Max,w[i]);//建第一种边 } for(int i=1;i<=n;i++){ scanf("%d",&b[i]); add(i,n+b[i]);//见第二种边 } for(int i=1;i<=Max;i++){ for(int j=2;j*i<=Max;j++){//第三种边 add(i+n,j*i+n); } } if(n==1){//数据特判,详见样例1 printf("1 ");clear();continue; } for(int i=1;i<=n;i++){//tarjan if(!dfn[i])dfs(i); } int ans=0; for(int i=1;i<=n;i++){//判断答案,注意只要1——n的点 if(vis[i])ans++; } printf("%d ",ans); clear(); } return 0; }
希望大家能学会这种神奇的建边方式