1. 程式人生 > >HDU 3639 強連通加縮點

HDU 3639 強連通加縮點

///先瞎做一番試試,我的實力的確是應該好好漲漲了。
//總算過了。。。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<string>
#define inf 0x3f3f3f3f
#define maxn 100500
using namespace std;

int n;//n m 為點數和邊數
int sum;
int head[maxn], edgenum;
int minw[maxn];
int DFN[maxn], Low[maxn], Stack[maxn], top, Time; //Low[u]是點集{u點及以u點為根的子樹} 中(所有反向弧)能指向的(離根最近的祖先v) 的DFN[v]值(即v點時間戳)
int taj;//連通分支標號,從1開始
int Belong[maxn];//Belong[i] 表示i點屬於的連通分支
bool Instack[maxn];
vector<int> bcc[maxn]; //標號從1開始
bool vis[maxn];
vector<int>G[maxn];
int du[maxn];
int gu[maxn];

struct Edge
{
    int from, to, nex;
    bool sign;//是否為橋
}edge[60000];

void addedge(int u, int v)
{//邊的起點和終點
    Edge E={u, v, head[u], false};
    edge[edgenum] = E;
    head[u] = edgenum++;
}



void tarjan(int u ,int fa)//求出圖的強聯通分量
{
    DFN[u] = Low[u] = ++ Time ;
    Stack[top ++ ] = u ;
    Instack[u] = 1 ;

    for (int i = head[u] ; ~i ; i = edge[i].nex )
	{
        int v = edge[i].to ;
        if(DFN[v] == -1)
        {
            tarjan(v , u) ;
            Low[u] = min(Low[u] ,Low[v]) ;
            if(DFN[u] < Low[v])
            {
                edge[i].sign = 1;//為割橋
            }
        }
        else if(Instack[v]) Low[u] = min(Low[u] ,DFN[v]) ;
    }
    if(Low[u] == DFN[u])
	{
        int now;
        taj ++ ;
		bcc[taj].clear();
        do
		{
            now = Stack[-- top] ;
            Instack[now] = 0 ;
            Belong [now] = taj ;
            bcc[taj].push_back(now);
        }while(now != u) ;
    }
}


void tarjan_init(int all)
{
    memset(DFN, -1, sizeof(DFN));
    memset(Instack, 0, sizeof(Instack));
    top = Time = taj = 0;
    for(int i=1;i<=all;i++)if(DFN[i]==-1 )tarjan(i, i); //注意開始點標!!!
}



void suodian()////還要更新出入度。。
{
    memset(du, 0, sizeof(du));
    memset(gu,0,sizeof(gu));
    memset(minw,0,sizeof(minw));
    for(int i = 1; i <= taj; i++)G[i].clear();
    for(int i = 0; i < edgenum; i++)
	{
        int u = Belong[edge[i].from], v = Belong[edge[i].to];
        if(u!=v)G[v].push_back(u), du[u]++,gu[v]++;///g[u]存的是出度,du[v]存的是入度
    }
    for(int i=1;i<=n;i++)
        minw[Belong[i]]++;
}


void init(){memset(head, -1, sizeof(head)); edgenum=0;}


///遍歷圖、、建反向圖然後深搜。。


void dfs(int u)///入度為0的點。。
{

    vis[u]=true;
    sum+=minw[u];
    for(int i=0;i<G[u].size();i++)
    {
        if(!vis[G[u][i]])
            dfs(G[u][i]);
    }


//反向建圖是因為要找的是可到達該點的強連同分量。。
}

int allans[maxn];
int TOT[maxn];
void solve()
{
    memset(allans,0,sizeof(allans));
    int maxx=0;int index=0;
    for(int i=1;i<=taj;i++)
    {
        if(du[i]==0)
        {
            memset(vis,false,sizeof(vis));
            sum=0;
            dfs(i);
            allans[i]=sum;
            if(allans[i]>maxx)
            {
                index=i;
                maxx=allans[i];
            }
        }
    }
     cout<<maxx-1<<endl;
     int tot=0;
//     cout<<taj<<" taj"<<endl;
//     for(int i=1;i<=n;i++)
//        cout<<Belong[i]<<endl;
//     cout<<"測試。。。"<<endl;
//     cout<<index<<endl;
//     for(int i=1;i<=n;i++)
//        if(Belong[i]==index)
//        cout<<i<<" ";
//     cout<<endl;
//     cout<<"-----------------------------"<<endl;
     for(int i=1;i<=n;i++)
     {
         if(allans[Belong[i]]==maxx)
           TOT[++tot]=i;
     }
     for(int i=1;i<tot;i++)
        cout<<TOT[i]-1<<" ";
     cout<<TOT[tot]-1<<endl;
}

int  main()
{
//    freopen("inn.txt","r",stdin);
    int cas,a,b;
    scanf("%d",&cas);
    int ca=0;
    while(cas--)
    {
        ca++;
        init();
        int m;
        scanf("%d%d",&n,&m);
        while(m--)
        {
            scanf("%d%d",&a,&b);
            a++,b++;
            addedge(a,b);
        }
        tarjan_init(n);
        suodian();
       cout<<"Case "<<ca<<": ";
       solve();
    }
}