codevs 2822 爱在心中 tarjan(强联通分量)

Posted lpl_bys

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了codevs 2822 爱在心中 tarjan(强联通分量)相关的知识,希望对你有一定的参考价值。

2822 爱在心中

 

 时间限制: 1 s
 空间限制: 128000 KB
 题目等级 : 钻石 Diamond
 
 
题目描述 Description

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

在爱的国度里有N个人,在他们的心中都有着一个爱的名单,上面记载着他所爱的人(不会出现自爱的情况)。爱是具有传递性的,即如果A爱B,B爱C,则A也爱C。
如果有这样一部分人,他们彼此都相爱,则他们就超越了一切的限制,用集体的爱化身成为一个爱心天使。
现在,我们想知道在这个爱的国度里会出现多少爱心天使。而且,如果某个爱心天使被其他所有人或爱心天使所爱则请输出这个爱心天使是由哪些人构成的,否则输出-1。

输入描述 Input Description

第1行,两个数N、M,代表爱的国度里有N个人,爱的关系有M条。
第2到第M+1行,每行两个数A、B,代表A爱B。

输出描述 Output Description

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

样例输入 Sample Input

样例输入1:

6 7
1 2
2 3
3 2
4 2
4 5
5 6
6 4


样例输入2:

3 3
1 2
2 1
2 3

样例输出 Sample Output

样例输出1:

2
2 3

样例输出2:

1
-1

数据范围及提示 Data Size & Hint

各个测试点1s

-------------------------------------------------------------------------------------------------------------------

这道题的第一问,先用tarjan找出图中所有的强联通分量,找出两个点及以上的强联通分量即题中的爱心天使

那第二问呢?

我们需要一步缩点操作

就是把每个强联通分量缩成一个点,建成另一个新的图,而且这个图肯定不带环(树或森林)

然后找出“爱心天使”中出度为0的点,从这个点开始遍历新图的反图,倘若所有点都能够遍历到,则说明“这个爱心天使被其他所有人或爱心天使所爱”,否则就不行

那为什么要找出度为0的点呢?

因为按题目的定义,这个爱心天使要能被所有点到达,又新图中不带环,所以不能有出度;从这里也可以看出满足条件的点最多只有一个

 

AC代码:

 

技术分享
 1 #include<stdio.h>
 2 #include<string.h>
 3 #define maxn 10010
 4 struct node{
 5     int to,next,fr;
 6 };
 7 node e[maxn],tr[maxn];
 8 int n,m,belong[maxn],dfn[maxn],low[maxn],stack[maxn],cnt,pre[maxn],time,top,bcnt,pretr[maxn],wrt,size[maxn],dcnt,po[maxn],pref[maxn];
 9 bool instack[maxn],pd[maxn];
10 void buildtr(int,int);
11 void tarjan(int);
12 void build(int,int);
13 void dfs(int);
14 int read();
15 int main(){
16     n=read();m=read();time=0;top=0;bcnt=0;wrt=0;dcnt=0;
17     for(int i=1;i<=m;i++){
18         int a=read(),b=read();
19         build(a,b);
20     }
21     for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
22     for(int i=1;i<=bcnt;i++) if(size[i]>1) po[++dcnt]=i;//第一问 
23     printf("%d\n",dcnt);  
24     cnt=0;
25     for(int i=1;i<=m;i++)
26        if(belong[e[i].to]!=belong[e[i].fr])
27        buildtr(belong[e[i].fr],belong[e[i].to]);
28     for(int i=1;i<=dcnt;i++)
29        if(!pretr[po[i]]){
30            memset(pd,0,sizeof(pd));
31            dfs(po[i]);
32            bool flag=true;
33            for(int j=1;j<=bcnt;j++)
34               if(!pd[j]){
35                   flag=false;
36                   break;
37               }
38            if(flag) wrt=po[i];
39            break;//因为满足条件的点只有一个,而且如果这个点不满足,往后也不满足,所以找到一个点出度为0的时候就可以退出了 ,
40        }
41     if(!wrt) printf("-1");
42     else
43        for(int i=1;i<=n;i++)
44            if(belong[i]==wrt)
45            printf("%d ",i);
46     return 0;
47 }
48 void dfs(int x){
49     pd[x]=1;
50     for(int i=pref[x];i;i=tr[i].next) dfs(tr[i].to);
51 }
52 void buildtr(int x,int y){
53     tr[++cnt].to=y;tr[cnt].next=pretr[x];pretr[x]=cnt;//新图 
54     tr[++cnt].to=x;tr[cnt].next=pref[y];pref[y]=cnt;//新图的反图 
55 }
56 void tarjan(int x){
57     dfn[x]=low[x]=++time;
58     instack[x]=1;stack[++top]=x;
59     for(int i=pre[x];i;i=e[i].next){
60         int to=e[i].to;
61         if(!dfn[to]){
62             tarjan(to);
63             if(low[to]<low[x]) low[x]=low[to];
64         }
65         else if(instack[to]&&dfn[to]<low[x]) low[x]=dfn[to];        
66     }
67     if(dfn[x]==low[x]){
68         bcnt++;
69         int k;
70         do{
71             k=stack[top--];
72             instack[k]=0;
73             belong[k]=bcnt;
74             size[bcnt]++;
75         }while(k!=x);
76     }
77 }
78 void build(int x,int y){
79     e[++cnt].to=y;e[cnt].next=pre[x];pre[x]=cnt;e[cnt].fr=x;
80 }
81 int read(){
82     int ans=0,f=1;char c=getchar();
83     while(0>c||c>9){if(c==-)f=-1;c=getchar();}
84     while(0<=c&&c<=9)ans=ans*10+c-48,c=getchar();return ans*f;
85 }
tarjan

 

以上是关于codevs 2822 爱在心中 tarjan(强联通分量)的主要内容,如果未能解决你的问题,请参考以下文章

[CodeVS2822]爱在心中

codevs 2822 爱在心中

codevs2822爱在心中

codevs2822爱在心中

codevs 2822 爱在心中

Tarjan笔记1