首页 > 代码库 > 【强连通分量】vijos 1626 爱在心中

【强连通分量】vijos 1626 爱在心中

描述

“每个人都拥有一个梦,即使彼此不相同,能够与你分享,无论失败成功都会感动。爱因为在心中,平凡而不平庸,世界就像迷宫,却又让我们此刻相逢Our Home。”

在爱的国度里有N个人,在他们的心中都有着一个爱的名单,上面记载着他所爱的人(不会出现自爱的情况)。爱是具有传递性的,即如果A爱B,B爱C,则A也爱C。

如果有这样一部分人,他们彼此都相爱,则他们就超越了一切的限制,用集体的爱化身成为一个爱心天使。

现在,我们想知道在这个爱的国度里会出现多少爱心天使。而且,如果某个爱心天使被其他所有人或爱心天使所爱则请输出这个爱心天使是由哪些人构成的,否则输出-1。

格式

输入格式

第1行,两个数N、M,代表爱的国度里有N个人,爱的关系有M条。

第2到第M+1行,每行两个数A、B,代表A爱B。

输出格式

第1行,一个数,代表爱的国度里有多少爱心天使。

第2行,如果某个爱心天使被其他所有人和爱心天使所爱则请输出这个爱心天使是由哪些人构成的(从小到大排序),否则输出-1。

样例1

样例输入1[复制]

6 71 22 33 24 24 55 66 4

样例输出1[复制]

22 3

样例2

样例输入2[复制]

3 31 22 12 3

样例输出2[复制]

1-1

限制

各个测试点1s

提示

对于40%的数据 N<=10 M<=100
对于80%的数据 N<=100 M<=1000
对于100%的数据 N<=1000 M<=10000

来源

Cai0715 原创
NOIP 2009·Dream Team 模拟赛 第一期 第四题

强连通分量+缩点

因为缩点后的图为有向无环图

所以总会有出度(out_degree)为0的连通分量

而如果出度为0的点只有一个

不难证明所有的连通分量都指向他

PS:

一:这题只有一个人的连通分量不算爱心天使

二:缩点后找到出度为0的点要判断个数 大于1个为-1

三:如果只有一个并且这个点不是一个人

# include<cstdio># include<cstring># include<stack># include<algorithm># include<iostream>using namespace std;const int N=1000+10;const int M=10000+10;stack<int>S;int n,m,ecnt,scc_cnt,dfs_clock,tot,out_degree,cur;int fist[N],next[M],v[M],pre[N],low[N],scc_no[N],size[N],out[N];void built(int a,int b){    ++ecnt;    v[ecnt]=b;    next[ecnt]=fist[a];    fist[a]=ecnt;}void init(){    int a,b;    memset(fist,-1,sizeof(fist));    scanf("%d%d",&n,&m);    for(int i=1;i<=m;i++){        scanf("%d%d",&a,&b);        built(a,b);    }}int dfs(int u){    int lowu=pre[u]=++dfs_clock;    S.push(u);    for(int e=fist[u];e!=-1;e=next[e])    if(!pre[v[e]])    lowu=min(lowu,dfs(v[e]));    else if(!scc_no[v[e]])    lowu=min(lowu,pre[v[e]]);    low[u]=lowu;    if(low[u]==pre[u]){        scc_cnt++;        for(;;){            int x=S.top();S.pop();            scc_no[x]=scc_cnt;            if(x==u)break;        }    }    return low[u];}void find_scc(){    memset(pre,0,sizeof(pre));    memset(low,0,sizeof(low));    for(int i=1;i<=n;i++)    if(!pre[i])dfs(i);}void work(){    for(int i=1;i<=n;i++)size[scc_no[i]]++;    for(int i=1;i<=scc_cnt;i++)if(size[i]>1)tot++;    printf("%d\n",tot);    for(int i=1;i<=n;i++)for(int e=fist[i];e!=-1;e=next[e])    if(scc_no[i]!=scc_no[v[e]])out[scc_no[i]]++;//因为scc_no已经相当于并查集了     for(int i=1;i<=scc_cnt;i++)if(out[i]==0){out_degree=i;cur++;}    if(cur>1||size[out_degree]==1){printf("-1");return;}    for(int i=1;i<=n;i++)if(scc_no[i]==out_degree)printf("%d ",i);}int main(){    init();    find_scc();    work();    return 0;}

 

【强连通分量】vijos 1626 爱在心中