洛谷P1726 上白泽慧音 [Tarjan]
上白泽慧音
题目描述
在幻想乡,上白泽慧音是以知识渊博闻名的老师。春雪异变导致人间之里的很多道路都被大雪堵塞,使有的学生不能顺利地到达慧音所在的村庄。因此慧音决定换一个能够聚集最多人数的村庄作为新的教学地点。人间之里由N个村庄(编号为1..N)和M条道路组成,道路分为两种一种为单向通行的,一种为双向通行的,分别用1和2来标记。如果存在由村庄A到达村庄B的通路,那么我们认为可以从村庄A到达村庄B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们认为A,B是绝对连通的,记为<A,B>。绝对连通区域是指一个村庄的集合,在这个集合中任意两个村庄X,Y都满足<X,Y>。现在你的任务是,找出最大的绝对连通区域,并将这个绝对连通区域的村庄按编号依次输出。若存在两个最大的,输出字典序最小的,比如当存在1,3,4和2,5,6这两个最大连通区域时,输出的是1,3,4。
输入输出格式
输入格式:
第1行:两个正整数N,M
第2..M+1行:每行三个正整数a,b,t, t = 1表示存在从村庄a到b的单向道路,t = 2表示村庄a,b之间存在双向通行的道路。保证每条道路只出现一次。
输出格式:
第1行: 1个整数,表示最大的绝对连通区域包含的村庄个数。
第2行:若干个整数,依次输出最大的绝对连通区域所包含的村庄编号。
输入输出样例
说明
对于60%的数据:N <= 200且M <= 10,000
对于100%的数据:N <= 5,000且M <= 50,000
分析:
很明显的$Tarjan$求强连通分量。
先把强连通分量全部求出来,每次统计每一个强连通分量的时候把它包含的点记录下来然后排个序,这样的话比较的时候如果大小相同,直接比较最小的点就行了,因为每一个点最多只可能属于一个强连通分量。($PS:$一开始把强连通分量内记录点的数组开小了然后$WA$了四个点,因为怕超空间。。。)
Code:
//It is made by HolseLee on 20th Aug 2018 //Luogu.org P1726 #include<bits/stdc++.h> #define Max(a,b) (a)>(b)?(a):(b) #define Min(a,b) (a)<(b)?(a):(b) using namespace std; const int N=5e3+7; const int M=5e4+7; int n,m,head[N],cnt,dfn[N],low[N],tot,idx; bool vis[N]; struct Node{ int to,nxt; }edge[M<<1]; struct SCC{ int val,s[5001]; bool operator < (const SCC x) const { return val==x.val?s[1]<x.s[1]:val>x.val; } }scc[2002]; stack<int>sta; inline int read() { char ch=getchar();int num=0;bool flag=false; while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();} while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();} return flag?-num:num; } inline void add(int x,int y) { edge[++cnt].to=y; edge[cnt].nxt=head[x]; head[x]=cnt; } void tarjan(int u) { dfn[u]=low[u]=++idx; sta.push(u);vis[u]=true; int v; for(int i=head[u];i!=-1;i=edge[i].nxt){ v=edge[i].to; if(!dfn[v]){ tarjan(v); low[u]=Min(low[u],low[v]); } else if(vis[v]){ low[u]=Min(low[u],dfn[v]); } } if(dfn[u]==low[u]){ scc[++tot].val=0; do{ v=sta.top();sta.pop(); vis[v]=false; scc[tot].val++; scc[tot].s[scc[tot].val]=v; }while(v!=u); } sort(scc[tot].s+1,scc[tot].s+1+scc[tot].val); } int main() { n=read();m=read(); memset(head,-1,sizeof(head)); int x,y,z; for(int i=1;i<=m;++i){ x=read(),y=read(),z=read(); add(x,y); if(z==2)add(y,x); } for(int i=1;i<=n;++i) if(!dfn[i])tarjan(i); sort(scc+1,scc+tot+1); printf("%d\n",scc[1].val); for(int i=1;i<=scc[1].val;++i){ printf("%d ",scc[1].s[i]); } return 0; }
如需转载,请署名作者并附上原文链接,蒟蒻非常感激
名称:HolseLee
博客地址:www.cnblogs.com/cytus
个人邮箱:1073133650@qq.com