HDU3639 Hawk-and-Chicken Tarjan缩点 +dfs+贪心

HDU3639 Hawk-and-Chicken :Tarjan缩点 +dfs+贪心

Description

一个N(0~n-1)个点与M条边的有向图,他们是有传递关系的,问对于图中哪些点是其他最多点能到达他的。

Input

第一行一个T,表示T组测试数据.
每组数据第一行为N,M
后面M行为两个正整数A,B,代表A到B有一条有向边.

Output

对应每组数据输出若干个答案.

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

HINT
题解

可以先缩点,再建一个每个连通块的反向图,这样最多的点肯定是出度为0的点。很容易证明。
如果假设最大值存在于一个入度不为0的分量中,那么连接这个分量的 那个分量票数支持肯定大于这个分量,假设不成立。
用dfs逐个搜索一遍就可以。

#include <cstdio>
#include <iostream>
#include <cmath>
#include <stack>
#include <algorithm>
#include <cstring>
#include <climits>
#include<vector>
#define MAXN 7000
#define MAXM 60000+10
#define M(x) memset(x,0,sizeof(x))
using namespace std;
int T;
struct Edge{
    int next,from,to,tnext;
};
struct G{
    int head[MAXN],num,tail[MAXN];
    Edge edge[MAXM*2];
    void add(int from,int to)
    {
        edge[++num].next=head[from];
        edge[num].tnext=tail[to];
        edge[num].from=from;
        edge[num].to=to;
        head[from]=num;tail[to]=num;
    }
    void e()
    {
        num=0;
        M(head);M(tail);M(edge);
    }
}g1,g2;
int n,m;
int low[MAXN],dfn[MAXN],dfnum,cnt[MAXN],col[MAXN],co,vis[MAXN],vis2[MAXN];
int ans[MAXN],ans2[MAXN],maxn;
vector<int> con[MAXN];
stack<int> st;
void tarjan(int x)
{
    dfn[x]=low[x]=++dfnum;vis[x]=1;st.push(x);
    for(int i=g1.head[x];i;i=g1.edge[i].next)
    {
        if(!dfn[g1.edge[i].to])
        {
            tarjan(g1.edge[i].to);
            low[x]=min(low[x],low[g1.edge[i].to]);
        }else if(vis[g1.edge[i].to]) low[x]=min(low[x],dfn[g1.edge[i].to]);
    }
    if(low[x]==dfn[x])
    {
        int t=1;col[x]=++co;vis[x]=0;
        con[co].push_back(x);
        while(st.top()!=x) 
        {con[co].push_back(st.top());t++;vis[st.top()]=0;col[st.top()]=co;st.pop();}
        st.pop();cnt[co]=t;
    }
}
int cu[MAXN];
int dfs(int x)
{   
    vis2[x]=1;
    int sum=0;
    for(int i=g2.tail[x];i;i=g2.edge[i].tnext)
    if(!vis2[g2.edge[i].from])
    {   
        sum+=cnt[g2.edge[i].from]+
        dfs(g2.edge[i].from);
    }
    return sum;
}
void init()
{
    maxn=co=dfnum=0;
    g1.e();g2.e();M(ans);M(ans2);M(cnt);M(col);
    M(con);M(vis);M(vis2);M(low);M(dfn);M(cu);
    while(st.size()) st.pop();
}
int main()
{
    scanf("%d",&T);
    for(int o=1;o<=T;o++)
    {
        init();
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;i++)
        {   
            int x,y;
            scanf("%d%d",&x,&y);
            g1.add(x+1,y+1);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i]) tarjan(i);
        for(int i=1;i<=m;i++)
        {   
            if(col[g1.edge[i].from]!=col[g1.edge[i].to])
                g2.add(col[g1.edge[i].from],col[g1.edge[i].to]),
                cu[col[g1.edge[i].from]]++;
        }
        for(int i=1;i<=co;i++)
        {   
            if(!cu[i])
            {M(vis2);ans2[i]=dfs(i)+cnt[i]-1;maxn=max(maxn,ans2[i]);}
        }

        for(int i=1;i<=co;i++)
        {   
            if(ans2[i]==maxn)
            for(int j=0;j<cnt[i];j++) ans[++ans[0]]=con[i][j];
        }
        sort(ans+1,ans+ans[0]+1);
        printf("Case %d: %d\n",o,maxn);
        printf("%d",ans[1]-1);
        for(int i=2;i<=ans[0];i++)
        printf(" %d",ans[i]-1);
        printf("\n");
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值