您的位置:首页 > 其它

Codevs2822爱在心中题解

2015-10-18 19:19 302 查看

题目

来源

https://vijos.org/p/1626

http://codevs.cn/problem/2822/

题目描述 Description

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

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

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

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

输入描述 Input Description

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

第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

题解

第一问,不难想到是直接强连通分量缩点,再记录强连通分量的大小。爱心天使是包含结点个数大于等于2的强连通分量。权当练一发非递归tarjan了。

第二问,初看起来有些复杂,但深入分析一下发现,其实也就那么回事。

缩完点后,肯定不存在环。若存在一个被所有人爱的爱心天使,那在有向图中体现为所有的点都直接或间接指向这个强连通分量,这个强连通分量不能有出边并且有且只有一个,证明如下:

1、若有某点不指向这个强连通分量,那由定义,这个强连通分量不是答案;

2、若这个强连通分量有出边,设这条出边指向的终点为t,由1,t不可能不指向这个强连通分量本身,那这个强连通分量就包含点t,出现矛盾,故这个强连通分量不可能有出边;

3、若这类强连通分量不唯一,那由2,这些强连通分量都没有出边,它们无法相连,这个爱心天使就不被所有人爱,不合题意,故若有这类强连通分量,有且只有一个。

那我们tarjan缩完点后,记录每个强连通分量的出度。之后枚举所有强连通分量,记录出度为零的强连通分量的个数。若最终个数不为1或者其大小不大于1,则无解;否则输出这个强连通分量的所有点。

Code

#include <cstdio>
#include <algorithm>
#include <cstring>
#define N 1005
#define M 10005
#define nil 0
using namespace std;
int n, m, oud
;
int u[M], v[M], nxt[M], pnt
, e;
int s
, top;
int S
, TOP, lst
;
int dfn
, low
, isin
, sz
, tot, indx;
bool ins
;
inline void add(int a, int b)
{
u[++e] = a; v[e] = b; nxt[e] = pnt[a]; pnt[a] = e;
}
void tarjan(int x)
{
dfn[x] = low[x] = ++indx;
S[++TOP] = s[++top] = x;
ins[x] = true;
while(TOP > 0)
{
int t = S[TOP];
for(int i = lst[t]; i != nil; i = nxt[i])
{
if(dfn[v[i]] == 0)
{
lst[t] = nxt[i];
dfn[v[i]] = low[v[i]] = ++indx;
S[++TOP] = s[++top] = v[i];
ins[v[i]] = true;
break;
}
}
if(t == S[TOP])
{
for(int i = pnt[t]; i != nil; i = nxt[i])
{
if(dfn[v[i]] > dfn[t]) low[t] = min(low[t], low[v[i]]);
else if(ins[v[i]]) low[t] = min(low[t], dfn[v[i]]);
}
if(dfn[t] == low[t])
{
int j;
++tot;
do
{
j = s[top--];
ins[j] = false;
isin[j] = tot;
++sz[tot];
} while(j != t);
}
--TOP;
}
}
}
void init()
{
e = top = TOP = tot = indx = 0;
memset(u, 0, sizeof(u));
memset(v, 0, sizeof(v));
memset(nxt, 0, sizeof(nxt));
memset(pnt, 0, sizeof(pnt));
memset(dfn, 0, sizeof(dfn));
memset(low, 0, sizeof(low));
memset(isin, 0, sizeof(isin));
memset(sz, 0, sizeof(sz));
memset(ins, 0, sizeof(ins));
memset(lst, 0, sizeof(lst));
memset(oud, 0, sizeof(oud));
int a, b;
scanf("%d%d", &n, &m);
for(int i = 1; i <= m; ++i)
{
scanf("%d%d", &a, &b);
add(a, b);
}
for(int i = 1; i <= n; ++i) lst[i] = pnt[i];
}
void work()
{
for(int i = 1; i <= n; ++i) if(dfn[i] == 0)
{
tarjan(i);
}
int ans = 0;
for(int i = 1; i <= tot; ++i)
{
if(sz[i] > 1) ++ans;
}
printf("%d\n", ans);
for(int i = 1; i <= n; ++i) for(int j = pnt[i]; j != nil; j = nxt[j])
{
if(isin[i] != isin[v[j]]) ++oud[isin[i]];
}
int cnt = 0, poi = 0;
for(int i = 1; i <= tot; ++i) if(oud[i] == 0)
{
++cnt; poi = i;
}
if(cnt != 1 || sz[poi] <= 1) puts("-1");
else
{
for(int i = 1; i <= n; ++i)
{
if(isin[i] == poi) printf("%d ", i);
}
}
}
int main()
{
init();
work();
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  图论 强连通分量