bzoj [HAOI2006]受欢迎的牛 (强联通分量+缩点)

版权声明:转载注明下出处就行了。 https://blog.csdn.net/LJD201724114126/article/details/84474928

强联通分量参考神犇博客:

https://www.byvoid.com/zhs/blog/scc-tarjan

https://www.luogu.org/blog/styx-ferryman/chu-tan-tarjan-suan-fa-qiu-qiang-lian-tong-fen-liang-post

题目链接:传送门

题解:直接弄个强联通分量,然后枚举每条边,假设x ,y不在同一个强联通块,那么就让强联通块x出度加1,最后找到出度为0的强联通块就行了,但只能有一个,要是超过了一个,就不符合题意了。

代码如下:

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

const int maxn=50010;

int x[maxn],y[maxn];
int DFN[maxn],LOW[maxn],next[maxn],point[maxn],v[maxn],vis[maxn];
int belong[maxn],top,tot,cnt,sizes[maxn],mark[maxn],stacks[maxn],index;

void add(int x,int y){
    tot++;
    next[tot]=point[x];
    point[x]=tot;
    v[tot]=y;
}

void tarjan(int x) ///强联通模板
{
    stacks[++top]=x; ///此点进栈
    vis[x]=1;  ///标记是否已经访问过

    DFN[x]=LOW[x]=++index; ///给编号

    for(int i=point[x];i;i=next[i])
    {
        int j=v[i];
        if(!DFN[j]){
            tarjan(j);
            LOW[x]=min(LOW[x],LOW[j]);
        }
        else if(vis[j]) LOW[x]=min(LOW[x],LOW[j]);
    }

    if(LOW[x]==DFN[x])
    {
        cnt++; ///有向图强联通分量有多少块
        int item;
        do{
             item=stacks[top--];
            vis[item]=0;
            belong[item]=cnt; ///表示点item属于cnt强联通块
            sizes[cnt]++; ///表示cnt块有多少个点
        }while(item!=x);
    }
}

int main()
{

    int n,m;
    while(~scanf("%d%d",&n,&m))
    {

        for(int i=1;i<=m;i++){
            scanf("%d%d",&x[i],&y[i]);
            add(x[i],y[i]);
        }

        memset(DFN,0,sizeof(DFN)); ///初始化
        memset(LOW,0,sizeof(LOW));
        memset(vis,0,sizeof(vis));
        memset(vis,0,sizeof(vis));

        top=tot=index=0;
        for(int i=1;i<=n;i++)
        {
            if(!DFN[i]) tarjan(i);
        }

        memset(mark,0,sizeof(mark));
        for(int i=1;i<=m;i++){

            if(belong[x[i]]!=belong[y[i]]){ ///表示有两块
                mark[belong[x[i]]]++; ///在第一块初度加1
            }
        }

        int total=0,flag=true;

        for(int i=1;i<=cnt;i++)
        {
            if(!mark[i]){
                if(total){ ///表示至少有两块出度为0的块,输出0
                    flag=false;break;
                }
                total=i;
            }

        }
//        printf("total=%d\n",total);

        if(flag) printf("%d\n",sizes[total]); ///只要一块出度为0的块
        else printf("0\n");
    }

    return 0;
}

我的标签:做个有情怀的程序员。

猜你喜欢

转载自blog.csdn.net/LJD201724114126/article/details/84474928
今日推荐