[GZOI2017]小z玩游戏

题目链接:[GZOI2017]小z玩游戏


暴力对可达点之间连边,边数的复杂度为 n*n

但是其实我们可以用中间点来转接,边数就变为 n * log(n) 了。

然后求SCC即可。


AC代码:

#pragma GCC optimize("-Ofast","-funroll-all-loops")
#include
//#define int long long
using namespace std;
const int N=3e5+10;
int n,up,w[N],e[N],low[N],dfn[N],scc[N],num[N],co,cnt,vis[N],res;
vector<int> g[N]; stack<int> s;
inline void init(){
     
	co=cnt=res=0;
	for(int i=1;i<=n+2*up;i++)	low[i]=dfn[i]=scc[i]=num[i]=0,g[i].clear();
}
void Tarjan(int x){
     
	low[x]=dfn[x]=++cnt; s.push(x),vis[x]=1;
	for(auto to:g[x]){
     
		if(!dfn[to]) Tarjan(to),low[x]=min(low[x],low[to]);
		else if(vis[to]) low[x]=min(low[x],dfn[to]);
	}
	if(dfn[x]==low[x]){
     
		int u; co++;
		do{
     
			u=s.top(); s.pop(); vis[u]=0; scc[u]=co; num[co]++;
		}while(u!=x);
	}
}
inline void solve(){
     
	cin>>n; up=0;
	for(int i=1;i<=n;i++)	scanf("%d",&w[i]),up=max(up,w[i]);
	for(int i=1;i<=n;i++)	scanf("%d",&e[i]),up=max(up,e[i]);
	init();
	for(int i=1;i<=up;i++)	for(int j=i;j<=up;j+=i)	g[i+up+n].push_back(j);
	for(int i=1;i<=n;i++)	g[w[i]].push_back(up+i),g[up+i].push_back(e[i]+n+up);
	for(int i=1;i<=n+2*up;i++)	if(!dfn[i])	Tarjan(i);
	for(int i=1;i<=n;i++)	if(num[scc[up+i]]>=2)	res++;
	printf("%d\n",res);
}
signed main(){
     
	int T;	cin>>T;
	while(T--)	solve();
	return 0;
}

你可能感兴趣的:(图论,强连通分量)