跳转至

练习

[USACO03FALL / HAOI2006] 受欢迎的牛 G

每头奶牛都梦想成为牛棚里的明星。被所有奶牛喜欢的奶牛就是一头明星奶牛。所有奶牛都是自恋狂,每头奶牛总是喜欢自己的。奶牛之间的“喜欢”是可以传递的——如果 \(A\) 喜欢 \(B\)\(B\) 喜欢 \(C\),那么 \(A\) 也喜欢 \(C\)。牛栏里共有 \(N\) 头奶牛,给定一些奶牛之间的爱慕关系,请你算出有多少头奶牛可以当明星。

输入格式

第一行:两个用空格分开的整数:\(N\)\(M\)

接下来 \(M\) 行:每行两个用空格分开的整数:\(A\)\(B\),表示 \(A\) 喜欢 \(B\)


缩点后会变成一棵树。找到没有出度的那个点u,u中包含是所有奶牛都是明星。如果有多个点没有出度,则答案为0,即奶牛分成了若干个阵营,相互不连通。

/*////////ACACACACACACAC///////////
       . Coding by Ntsc .
       . Prove Yourself .
/*////////ACACACACACACAC///////////

#include<bits/stdc++.h>
#define ll long long
#define db double
#define rtn return
#define i1n int i=1;i<=n;i++
#define in1 int i=n;i>=1;i--
using namespace std;

const int N=5e5+5;
const int M=1e5;
const int Mod=1e5;
const int INF=1e5;

int dfn[N],low[N],stk[N],tot,top,cnt,scc[N],siz[N],sccw[N],w[N],dis[N],vis[N],n,m,way[N][2],instk[N],s,np,p[N],ans;
vector <int> e[N];
int du[N];
int f[N];


vector<int>e2[N];

void add(int a,int b){
    e[a].push_back(b);
}
void add2(int a,int b){
    e2[a].push_back(b);
    du[a]++;
}

void tarjan(int x){//强连通分量缩点
    //入x时,时间戳追溯值更新,入栈
    dfn[x]=low[x]=++tot;
    stk[++top]=x;instk[x]=1; 

    for(int i=0;i<e[x].size();i++){//枚举x的邻点y 
        int y=e[x][i];
        if(!dfn[y]){//如果y还没有访问过 
            tarjan(y);//向下走 
            low[x]=min(low[x],low[y]);//返回时更新 
        }else if(dfn[y]&&instk[y]){//说明 y被访问过 ->要么y是祖先(在x出通过返祖边访问到了),要么是左子树的点(在x通过横插边访问到了) 
            low[x]=min(low[x],dfn[y]); 
        }
    }
    if(dfn[x]==low[x]){//说明x是这个强连通分量的根 
        int y;++cnt;
        int flag=0;
        do{
            flag++;
            y=stk[top--];instk[y]=0;
            scc[y]=cnt;
            ++siz[cnt];
            sccw[cnt]+=w[y];//记录缩点后强连通分量点的点权
        } while(y!=x); 
    }
}

void dfs(int x){
    if(f[x])return ;
    f[x]=sccw[x];
    int mx=0;
    for(auto v:e2[x]){
        if(!f[v])dfs(v);//搜过就不用再搜,直接调用 
        mx=max(mx,f[v]);
    }
    f[x]+=mx;
}

signed main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>way[i][0]>>way[i][1];
    }

    for(int i=1;i<=m;i++){
        add(way[i][0],way[i][1]);
    }


    for(int i=1;i<=n;i++)//题目不保证图连续!
        if(!dfn[i]) tarjan(i);

    //建新图 
    for(int i=1;i<=n;i++){
        for(int j=0;j<e[i].size();j++){
            int v=e[i][j];
            if(scc[i]==scc[v])continue;//注意continue
            add2(scc[i],scc[v]);//注意不是scc[j]
        }
    }
    int res=0;
    for(int i=1;i<=cnt;i++){
        if(!du[i])res++,ans=siz[i];
        if(res>1){
            ans=0;break;
        }
    } 

    cout<<ans<<endl;
    return 0;
}