题目描述:
大学班级选班长,N 个同学均可以发表意见 若意见为 A B 则表示 A 认为 B 合适,意见具有传递性,即 A 认为 B 合适,B 认为 C 合适,则 A 也认为 C 合适 勤劳的 TT 收集了M条意见,想要知道最高票数,并给出一份候选人名单,即所有得票最多的同学,你能帮帮他吗?
input:
本题有多组数据。第一行 T 表示数据组数。每组数据开始有两个整数 N 和 M (2 <= n <= 5000, 0 <m <= 30000),接下来有 M 行包含两个整数 A 和 B(A != B) 表示 A 认为 B 合适。
output:
对于每组数据,第一行输出 “Case x: ”,x 表示数据的编号,从1开始,紧跟着是最高的票数。 接下来一行输出得票最多的同学的编号,用空格隔开,不忽略行末空格!
思路:
如果某点在环中,则该点受到的投票为环中的点数-1。
先用Kosaraju求出强连通分量,然后缩点:两边dfs,第一遍求出逆后序序列,第二遍根据逆后序序列找出所有强连通分量。
稍加思考,可以发现最后答案一定出现在出度为 0 的 SCC 中。因此遍历出度为0的点,统计其所能到达的所有的点。
技巧:1.缩点之后两个点A和B之间会有很多条边,这里只建立一条边,防止时间复杂度过高。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=5050;
int n,m,maxx,scnt,cont;
int tot,totp,totpp;
int head[maxn],headp[maxn],headpp[maxn];
int ans[maxn],du[maxn],scc[maxn],c[maxn],dfn[maxn];
bool flag[maxn],flag_scc[maxn],flag_ans[maxn],if_have_edge[maxn][maxn];
struct node
{
int v;
int next;
}e[30050],ep[30050],epp[30050];
void add_edge(int u,int v)//原图
{
e[++tot].v=v;
e[tot].next=head[u];
head[u]=tot;
}
void add_edge_p(int u,int v)//反图
{
ep[++totp].v=v;
ep[totp].next=headp[u];
headp[u]=totp;
}
void add_edge_pp(int u,int v)//缩点后的反向图
{
epp[++totpp].v=v;
epp[totpp].next=headpp[u];
headpp[u]=totpp;
}
void dfs(int u)
{
flag[u]=1;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].v;
if(!flag[v])
dfs(v);
}
dfn[++cont]=u;
}
void dfs2(int u)
{
c[u]=scnt;scc[scnt]++;
for(int i=headp[u];i;i=ep[i].next)
{
int v=ep[i].v;
if(!c[v])
dfs2(v);
}
}
void dfs3(int u,int s)
{
flag_scc[u]=1;
for(int i=headpp[u];i;i=epp[i].next)
{
int v=epp[i].v;
if(!flag_scc[v])
{
flag_scc[v]=1;
ans[s]+=scc[v];
dfs3(v,s);
}
}
}
int init()
{
int f=1,pp=0;char c=getchar();
while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9'){pp=pp*10+c-'0';c=getchar();}
return f*pp;
}
void ini()
{
memset(head,0,sizeof(head));
memset(headp,0,sizeof(headp));
memset(headpp,0,sizeof(headpp));
memset(dfn,0,sizeof(dfn));
memset(scc,0,sizeof(scc));
memset(du,0,sizeof(du));
memset(c,0,sizeof(c));
memset(ans,0,sizeof(ans));
memset(flag,0,sizeof(flag));
memset(if_have_edge,0,sizeof(if_have_edge));
memset(flag_ans,0,sizeof(flag_ans));
tot=0;totp=0;totpp=0;cont=0;scnt=0;maxx=0;
}
int main()
{
int T,x,y;
T=init();
for(int k=1;k<=T;k++)
{
ini();
n=init();m=init();
for(int i=1;i<=m;i++)
{
x=init();y=init();
add_edge(x+1,y+1);
add_edge_p(y+1,x+1);
}
for(int i=1;i<=n;i++)
if(!flag[i]) dfs(i);
for(int i=n;i>=1;i--)
if(!c[dfn[i]])
{
scnt++;
dfs2(dfn[i]);
}
for(int i=1;i<=n;i++)
for(int j=head[i];j;j=e[j].next)
{
int v=e[j].v;
if(c[i]!=c[v]&&!if_have_edge[c[v]][c[i]])
{
if_have_edge[c[v]][c[i]]=1;
add_edge_pp(c[v],c[i]);
du[c[i]]++;
}
}
int maxx=-1;
for(int i=1;i<=scnt;i++)
if(du[i]==0)
{
memset(flag_scc,0,sizeof(flag_scc));
ans[i]+=scc[i]-1;
dfs3(i,i);
maxx=max(maxx,ans[i]);
}
for(int i=1;i<=scnt;i++)
if(ans[i]==maxx)
flag_ans[i]=1;
cout<<"Case "<<k<<": "<<maxx<<endl;
bool flag_tmp=0;
for(int i=1;i<=n;i++)
if(flag_ans[c[i]])
{
if(!flag_tmp)
{
cout<<i-1;
flag_tmp=1;
}
else cout<<" "<<i-1;
}
cout<<endl;
}
return 0;
}