题目描述
小J在读研究生时,经常给中学的同学们当家教(PS:小J是天枰座,他有特殊的癖好,就是每个中学带的学生一样多,这样能保证教育的“公平性”)。这些同学往往放学后,从学校出发去小J家补课。这天,郑州下了场鹅毛大雪,小J一看,完了,很多同学都无法来自己家上课了。因此,他要选取某所学校作为上课地点,然后通知一部分学校能到的学生来上课。郑州市有N个中学(编号为 1..N),其间有M条道路。下雪后,由于路滑,经常会有车辆出现碰撞、剐蹭,使得路况出现了两种情况,一种为单向通行的,一种为双向通行的,分别用 1 和 2 来标记。如果存在由学校A到达学校 B 的通路,那么我们认为可以从A 到达 B,记为(A,B)。当(A,B)和(B,A)同时满足时,我们记为<A,B>。由于小J在前往预订学校途中,可能会出现意外情况,使得小J可能会临时更改学校,然后抓紧通知这些学生,放学后赶往新地点。因此,这就要求小J计划通知这些学生所在的学校中,任意两个学校X, Y间都满足<X, Y>。现在小J想给最多的学生上课,请你帮他求出计划通知学生所在学校的编号。若存在两个最大的,输出字典序最小的,比如当存在 1,3,4和 2,5,6 这两个最大连通区域时,输出的是 1,3,4。
输入
第 1 行:两个正整数 N,M (N <= 5,000 且 M <= 50,000)
第 2..M+1 行:每行三个正整数 a, b, t。(t = 1 表示存在从学校 a 到 b 的单向道路, t = 2 表示学校a,b 之间存在双向通行的道路。 保证每条道路只出现一次。)
输出
第 1 行: 1 个整数,表示最多学校个数。
第 2 行:若干个整数,依次输出学校编号,用空格隔开。
样例输入
5 5
1 2 1
1 3 2
2 4 2
5 1 2
3 5 1
样例输出
3
1 3 5
思路:
两种方法:tarjan或kosaraju
kosaraju代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=5010,mod=32767;
int vis[N],cmp[N],cnt[N];
vector<int>G[N],rG[N];
vector<int>vs;
void add(int from,int to){
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v){
vis[v]=1;
for(int i=0;i<G[v].size();i++){
if(!vis[G[v][i]])dfs(G[v][i]);
}
vs.push_back(v);
}
void rdfs(int v,int k){
vis[v]=1;
cmp[v]=k;
cnt[k]++;
for(int i=0;i<rG[v].size();i++){
if(!vis[rG[v][i]])rdfs(rG[v][i],k);
}
}
int main(){
int n,m,a,b,c;
scanf("%d%d",&n,&m);
memset(vis,0,sizeof(vis));
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&c);
if(c==1)add(a,b);
else {add(a,b);add(b,a);}
}
for(int i=1;i<=n;i++){
if(!vis[i])dfs(i);
}
memset(vis,0,sizeof(vis));
int k=0;
for(int i=vs.size()-1;i>=0;i--){
if(!vis[vs[i]])rdfs(vs[i],k++);
}
int mx=0,num;
for(int i=1;i<=n;i++){
int tmp=cmp[i];//属于的强联通分量的值
if(cnt[tmp]>mx){
mx=cnt[tmp];
num=tmp;
}
}
printf("%d\n",mx);
int tot=0;
for(int i=1;i<=n;i++){
if(cmp[i]==num){
tot++;
if(tot!=mx)printf("%d ",i);
else printf("%d\n",i);
}
}
}
tarjan算法代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<queue>
#include<stack>
#include<cmath>
#include<set>
#include<map>
using namespace std;
#define ll long long
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
typedef pair<int,int>P;
const int INF=0x3f3f3f3f;
const int N=5015,M=50005,mod=32767;
struct A{
int to,next;
}edge[2*M];
int head[N],cnt=0,sta[N],vis[N];
int Q,mx=0,dfn[N],low[N],tot,inde,k;
set<int>qqq[N];
void add(int from,int to){
edge[cnt].to=to;
edge[cnt].next=head[from];
head[from]=cnt++;
}
void tarjan(int x){
dfn[x]=low[x]=++tot;
sta[++inde]=x;
vis[x]=1;
for(int i=head[x];i!=-1;i=edge[i].next){
if(!dfn[edge[i].to]){
tarjan(edge[i].to);
low[x]=min(low[x],low[edge[i].to]);
}
else if(vis[edge[i].to]){
low[x]=min(low[x],dfn[edge[i].to]);
}
}
if(low[x]==dfn[x]){
k++;
int tmp=0;
do{
qqq[k].insert(sta[inde]);
tmp++;
vis[sta[inde]]=0;
inde--;
}while(x!=sta[inde+1]);
if(mx<tmp){Q=k;mx=tmp;}
}
return ;
}
int main(){
int n,m,a,b,t;
memset(head,-1,sizeof(head));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&a,&b,&t);
add(a,b);
if(t==2)add(b,a);
}
for(int i=1;i<=n;i++){
if(!dfn[i])tarjan(i);
}
printf("%d\n",mx);
set<int>::iterator it1,it2=qqq[Q].begin();
int tmp=*it2;
for(int i=1;i<=k;i++){
if(qqq[i].size()==mx){
it1=qqq[i].begin();
it2=qqq[Q].begin();
if((*it1)<(*it2))Q=i;
}
}
it1=qqq[Q].begin();
printf("%d",*it1);
it1++;
for(;it1!=qqq[Q].end();it1++){
printf(" %d",*it1);
}
printf("\n");
}