zoukankan      html  css  js  c++  java
  • 神奇的建图方式(Tarjan)——小z玩游戏

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

    题面:

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

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

    但是时间肯定不允许。

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

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

    那么如何建边,

    首先将点开多一些

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

    然后建边

    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;
    }
    

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

  • 相关阅读:
    [LeetCode] 735. Asteroid Collision
    [LeetCode] 14. Longest Common Prefix
    [LeetCode] 289. Game of Life
    [LeetCode] 73. Set Matrix Zeroes
    [LeetCode] 59. Spiral Matrix II
    [LeetCode] 54. Spiral Matrix
    [LeetCode] 48. Rotate Image
    [LeetCode] 134. Gas Station
    [LeetCode] 70. Climbing Stairs
    [LeetCode] 71. Simplify Path
  • 原文地址:https://www.cnblogs.com/DZN2004/p/13199361.html
Copyright © 2011-2022 走看看