hdu3639(强联通)

Kids in kindergarten enjoy playing a game called Hawk-and-Chicken. But there always exists a big problem: every kid in this game want to play the role of Hawk. 
So the teacher came up with an idea: Vote. Every child have some nice handkerchiefs, and if he/she think someone is suitable for the role of Hawk, he/she gives a handkerchief to this kid, which means this kid who is given the handkerchief win the support. Note the support can be transmitted. Kids who get the most supports win in the vote and able to play the role of Hawk.(A note:if A can win 
support from B(A != B) A can win only one support from B in any case the number of the supports transmitted from B to A are many. And A can't win the support from himself in any case. 
If two or more kids own the same number of support from others, we treat all of them as winner. 
Here's a sample: 3 kids A, B and C, A gives a handkerchief to B, B gives a handkerchief to C, so C wins 2 supports and he is choosen to be the Hawk.

Input

There are several test cases. First is a integer T(T <= 50), means the number of test cases. 
Each test case start with two integer n, m in a line (2 <= n <= 5000, 0 <m <= 30000). n means there are n children(numbered from 0 to n - 1). Each of the following m lines contains two integers A and B(A != B) denoting that the child numbered A give a handkerchief to B.

Output

For each test case, the output should first contain one line with "Case x:", here x means the case number start from 1. Followed by one number which is the totalsupports the winner(s) get. 
Then follow a line contain all the Hawks' number. The numbers must be listed in increasing order and separated by single spaces.

Sample Input

2
4 3
3 2
2 0
2 1

3 3
1 0
2 1
0 2

Sample Output

Case 1: 2
0 1
Case 2: 2
0 1 2

题意:
  给你一个有向图,如果从u点能到达v点,那么说u是v的粉丝,现在要你按序输出那些粉丝数目最多的点编号.

结论:原图中具有最多粉丝的点一定在新图的那些出度为0的点所代表的分量中.

        证明:假设u节点粉丝最多且它所属的分量出度不为0,那么u节点一定是某个节点v的粉丝,所以v的粉丝必然包含了u的所有粉丝加上u本身.所以v的粉丝必然多余u.由此矛盾.

        下面的问题是如何找新DAG图的每个节点(所代表分量中的原节点)的最大粉丝数? 该粉丝数=本连通分量的点数-1+本连通分量能通过ß这种边逆向走到的所有分量的点数和. 所以我们直接建立缩点树的逆图DAG即可,如果u->v表示u分量将获得v分量的所有节点作为粉丝.所以我们只需要对那几个入度为0的点做DFS即可.

        每次DFS到一个新节点,该点所代表的分量节点数就都加到sum上去,表示新加了很多粉丝.最后找最大粉丝值的分量点输出即可.
 

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
using namespace std;
const int maxn= 5000+10;
int n,m;
vector<int> G[maxn], G2[maxn];
stack<int> S;
int dfs_clock, scc_cnt;
int pre[maxn],sccno[maxn],low[maxn];
int num[maxn];//表每个强连通分量各含多少点
int in[maxn];//新DAG的逆图中点的入度
int fan[maxn];//表新DAG中第i个点(分量)有多少粉丝
void dfs(int u)
{
    pre[u]=low[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);
            low[u]=min(low[u],low[v]);
        }
        else if(!sccno[v])
            low[u]=min(low[u],pre[v]);
    }
    if(low[u]==pre[u])
    {
        scc_cnt++;
        num[scc_cnt]=0;
        while(true)
        {
            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(pre,0,sizeof(pre));
    memset(sccno,0,sizeof(sccno));
    for(int i=0;i<n;i++)
        if(!pre[i]) dfs(i);
}
bool vis[maxn];
int dfs2(int u)
{
    vis[u]=true;
    int sum=0;
    for(int i=0;i<G2[u].size();i++)
    {
        int v=G2[u][i];
        if(!vis[v]) sum+=num[v]+dfs2(v);  //WA-> vis[i] num[i] dfs2(i)
    }
    return sum;
}
int main()
{
    int T; scanf("%d",&T);
    for(int kase=1;kase<=T;kase++)
    {
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++) G[i].clear();
        while(m--)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            G[u].push_back(v);
        }
        find_scc(n);
 
        memset(in,0,sizeof(in));
        for(int i=1;i<=scc_cnt;i++) G2[i].clear();
        for(int u=0;u<n;u++)
        for(int i=0;i<G[u].size();i++)
        {
            int v=G[u][i];
            int x=sccno[u], y=sccno[v];
            if(x!=y)
            {
                in[x]++;
                G2[y].push_back(x);//建立DAG的逆图
            }
        }
        memset(fan,0,sizeof(fan));
 
        int max_f=-1;
        for(int i=1;i<=scc_cnt;i++)
            if(!in[i])
            {
                memset(vis,0,sizeof(vis)); //WA-> vis数组只在外面初始化1次
                fan[i] = num[i]-1+dfs2(i);
                max_f=max(fan[i],max_f);
            }
        bool win[maxn];
        memset(win,0,sizeof(win));
        for(int i=0;i<n;i++)
            if(fan[sccno[i]]==max_f) win[i]=true;
        printf("Case %d: %d\n",kase,max_f);
        bool first=true;
        for(int i=0;i<n;i++)if(win[i])
        {
            if(first) printf("%d",i), first=false;
            else printf(" %d",i);
        }
        puts("");
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值