题意:给你一个有向图,如果从u点能到达v点,那么说u是v的粉丝,现在要你按序输出那些粉丝数目最多的点编号.
思路:转自网上,写得很详细了
假设该图是一个强连通图,那么任一点都有n-1个粉丝(即n-1个点能到达它).所以我们把该图缩点变成一个新的DAG图.
结论:原图中具有最多粉丝的点一定在新图的那些出度为0的点所代表的分量中.
证明:假设u节点粉丝最多且它所属的分量出度不为0,那么u节点一定是某个节点v的粉丝,所以v的粉丝必然包含了u的所有粉丝加上u本身.所以v的粉丝必然多余u.由此矛盾.
下面的问题是如何找新DAG图的每个节点(所代表分量中的原节点)的最大粉丝数? 该粉丝数=本连通分量的点数-1+本连通分量能通过ß这种边逆向走到的所有分量的点数和. 所以我们直接建立缩点树的逆图DAG即可,如果u->v表示u分量将获得v分量的所有节点作为粉丝.所以我们只需要对那几个入度为0的点做DFS即可.
每次DFS到一个新节点,该点所代表的分量节点数就都加到sum上去,表示新加了很多粉丝.最后找最大粉丝值的分量点输出即可.
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <map>
#include <string>
#include <set>
#include <ctime>
#include <cmath>
#include <cctype>
#include <stack>
using namespace std;
#define maxn 100000+100
#define LL long long
int cas=1,T;
vector<int>G[maxn],NewG[maxn];
int pre[maxn];
int lowlink[maxn];
int sccno[maxn];
int num[maxn]; //在i编号scc中有多少个点
int dfs_clock,scc_cnt;
int n,m;
int fan[maxn]; //新的图中每个点的粉丝数
stack<int>S;
void dfs(int u)
{
pre[u]=lowlink[u]=++dfs_clock;
S.push(u);
for (int i = 0;i<G[u].size();i++)
{
int v = G[u][i];
if (!pre[v])
{
dfs(v);
lowlink[u] = min(lowlink[u],lowlink[v]);
}
else if (!sccno[v])
{
lowlink[u] = min (lowlink[u],pre[v]);
}
}
if (lowlink[u] == pre[u])
{
scc_cnt++;
// num[scc_cnt]=0;
for (;;)
{
int x = S.top();S.pop();
sccno[x] = scc_cnt;
num[scc_cnt]++;
if (x==u)
break;
}
}
}
void find_scc(int n)
{
dfs_clock=scc_cnt=0;
memset(sccno,0,sizeof(sccno));
memset(pre,0,sizeof(pre));
for (int i = 0;i<n;i++)
if (!pre[i])
dfs(i);
}
int in[maxn];
int out[maxn];
bool vis[maxn];
int dfs2(int u)
{
vis[u]=1;
int sum = 0;
for (int i = 0;i<NewG[u].size();i++)
{
int v = NewG[u][i];
if (!vis[v])
sum+=num[v]+dfs2(v);
}
return sum;
}
int main()
{
//freopen("in","r",stdin);
scanf("%d",&T);
while (T--)
{
// printf("Case %d: ",cas++);
scanf("%d%d",&n,&m);
for (int i = 0;i<=n;i++)
G[i].clear();
memset(out,0,sizeof(out));
memset(in,0,sizeof(in));
memset(num,0,sizeof(num));
for (int i = 1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
G[u].push_back(v);
}
find_scc(n);
for (int i = 0;i<=scc_cnt;i++)
{
out[i]=0;
in[i]=0;
NewG[i].clear();
}
for (int u = 0;u<n;u++)
for (int i =0;i<G[u].size();i++)
{
int v = G[u][i];
if (sccno[u] != sccno[v])
{
NewG[sccno[v]].push_back(sccno[u]);
in[sccno[u]]++;
}
}
memset(fan,0,sizeof(fan));
int maxf=-1;
for (int i = 1;i<=scc_cnt;i++)
{
if (!in[i])
{
memset(vis,0,sizeof(vis));
fan[i]=num[i]-1+dfs2(i);
maxf=max(fan[i],maxf);
}
}
bool win[maxn];
memset(win,0,sizeof(win));
for (int i = 0;i<n;i++)
if (fan[sccno[i]]==maxf)
win[i]=1;
printf("Case %d: %d\n",cas++,maxf);
bool first = 1;
for (int i = 0;i<n;i++)
if (win[i])
{
if (first)
printf("%d",i),first=0;
else
printf(" %d",i);
}
puts("");
}
return 0;
}