神奇的建图方式(Tarjan)——小z玩游戏

原题来自与:洛谷 P5676(GZOI2017)  链接: https://www.luogu.com.cn/problem/P5676

题面:

神奇的建图方式(Tarjan)——小z玩游戏_第1张图片

 

题意比较明显,如果已经建好了边,那么跑个Tarjan 就完了。

但是问题在于建边的复杂度,比较好想的是n 的建边方式。

但是时间肯定不允许。

那么我们就要想一种时间复杂度较小的建边方式。

可以考虑引入中间变量兴奋程度

那么如何建边,

首先将点开多一些

把中间变量兴奋程度也当作点

然后建边

1.建一个由 有趣程度 到 点 的边

2.建一个由 点 到 兴奋程度 的边

3.重点:建一个兴奋程度整数倍的边。

然后就跑一边tarjan就完了,

下面是代码(加注释):

#include 
#include 
#include 
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\n");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\n",ans);
		clear();
	}
	return 0;
}

 

希望大家能学会这种神奇的建边方式

 

你可能感兴趣的:(神奇的建图方式(Tarjan)——小z玩游戏)