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; }
相关文章推荐
- 初学图论-Kahn拓扑排序算法(Kahn's Topological Sort Algorithm)
- 初学图论-Bellman-Ford单源最短路径算法
- 初学图论-DAG单源最短路径算法
- 初学图论-Dijkstra单源最短路径算法
- 初学图论-Dijkstra单源最短路径算法基于优先级队列(Priority Queue)的实现
- hdu1827&&hdu2767----Kosaraju算法
- hdu3072&&hdu3639----Kosaraju算法
- 封装好的Folyd建图,C++源码
- LCA模板
- 图论学习笔记之一——Floyd算法
- 【LCA】SPOJ QTREE2
- poj 3249 Test for Job 最长路
- HDU 2544
- Timus 1557 Network Attack DFS+各种各种...
- HDU1289 Tarjan-模板题
- Poj2638 网络流+最短路+二分答案
- Aizu1311 分层图最短路 (...大概)
- HDU 3631 Shortest Path
- 二分图匹配模板
- 最短路径 -- spfa