UOJ #210UER #6寻找罪犯
Posted cot
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了UOJ #210UER #6寻找罪犯相关的知识,希望对你有一定的参考价值。
题目描述
通过一些不可描述的方式,妹滋滋算出了 51% 的得票率,于是就她就把这个公开给了广大用户 —— UOJ 解散已成定局。
几个小时后,UOJ 创始人伏特跳蚤国王宣布辞职,即日起退出 UOJ 团队。
这两个消息在算法竞赛界引起了轩然大波,“UOJ 是什么”“废除UOJ有什么影响” 马上成为了网民们的搜索热点并出现在了各大搜索网站的首页上。
著名的大水群和三连击发源地 —— Universal OJ 用户群随之解散,导致大量 OI 水狗们无处可水。一段时间后,圈子里渐渐传出了恢复 UOJ 的呼声,更有一些人将这个烂摊子归咎于那些投票通过的用户 —— 他们决定找出这些人并加以指责。
经过一段时间的搜索,他们找到了 n 个嫌疑人,编号为 1 到 n,导致 UOJ 解散的犯人就在他们之间。严刑拷打之下,他们交代了一些供词,供词有两类:
xi 说 yi 是犯人。
xi 说 yi 不是犯人。
然而,让事情变得复杂的是,犯人们并不打算背锅,所以他们的供词不总是真的,同时,为了不闹乌龙暴露自己,每一个犯人的所有供词最多有一句是假的,而不是犯人的嫌疑人的供词总是真的。
现在给出了全部的 mm 条供词,你需要找出哪些人是犯人。如果有多解,输出任何一组解即可。
输入格式
第一行两个正整数 n,m,表示犯人数目与供词数目。
接下来 m 行,每行三个整数 xi,yi,ti。其中 ti=0 表示 xi 说 yi 是犯人,ti=1 表示 xi 说 yi 不是犯人。
输出格式
第一行一个整数 c 表示犯人的数目。
第二行 c 个整数 pi,按照升序输出所有犯人的编号。
如果不存在一个犯人的集合使得供词满足条件,输出一行一个单词 "Impossible"。
题解
2-SAT。
将一个人拆成两个点,表示他是犯人和他不是犯人。若他不是犯人,那么他说的话都是对的,那么就可以通过他是犯人推出他说的人是不是犯人。如果有人说他是犯人,那么可以推出他肯定是犯人。若他是犯人,那么可以推出所有说他不是犯人的人一定是犯人。因为他只能说一句谎话,所有他说的话的反命题一定可以推出他的其他话一定是对的。然而这样的话边数是m^2的,所以用前/后缀和优化构图即可。
关于如何判无解与输出方案:
把可以推出的关系看做有向图的边,那么一个强连通分量就可以看做等价的命题。如果两个矛盾的命题是等价的,(即一个人既是犯人又不是犯人)那么就无解。
输出方案时,考虑强连通分量中的每一个点的反命题,如果反命题选了,那么这一整个强连通分量就不能选了,不然就可以选。
代码
1 #include <cstdio> 2 #include <algorithm> 3 4 #define R register 5 #define maxn 400010 6 #define cmin(_a, _b) (_a > (_b) ? _a = (_b) : 0) 7 inline int F() 8 { 9 R char ch; R int cnt = 0; 10 while (ch = getchar(), ch < ‘0‘ || ch > ‘9‘) ; 11 cnt = ch - ‘0‘; 12 while (ch = getchar(), ch >= ‘0‘ && ch <= ‘9‘) cnt = cnt * 10 + ch - ‘0‘; 13 return cnt; 14 } 15 struct Edge { 16 Edge *next; 17 int to; 18 } *last[maxn << 1], e[maxn << 2], *ecnt = e; 19 struct edge { 20 edge *next; int to, w; 21 } *lt[maxn], le[maxn << 2], *lecnt = le, *rt[maxn], re[maxn << 2], *recnt = re; 22 inline void link1(R int a, R int b, R int w) 23 { 24 *++lecnt = (edge) {lt[a], b, w}; lt[a] = lecnt; 25 *++recnt = (edge) {rt[b], a, w}; rt[b] = recnt; 26 } 27 inline void link(R int a, R int b) 28 { 29 // printf("%d %d\n", a, b); 30 *++ecnt = (Edge) {last[a], b}; last[a] = ecnt; 31 } 32 int dfn[maxn], low[maxn], timer, st[maxn], top, id[maxn], colcnt, n; 33 bool fail, used[maxn]; 34 void tarjan(R int x, R int fa) 35 { 36 dfn[x] = low[x] = ++timer; st[++top] = x; 37 for (R Edge *iter = last[x]; iter; iter = iter -> next) 38 if (iter -> to != fa) 39 { 40 if (!dfn[iter -> to]) 41 { 42 tarjan(iter -> to, x); 43 cmin(low[x], low[iter -> to]); 44 } 45 else if (!id[iter -> to]) cmin(low[x], dfn[iter -> to]); 46 } 47 if (dfn[x] == low[x]) 48 { 49 ++colcnt; R bool flag = 1; 50 for (; ;) 51 { 52 R int now = st[top--]; 53 id[now] = colcnt; 54 // printf("now %d colcnt %d\n", now, colcnt); 55 if (now <= 2 * n) 56 { 57 flag &= !used[id[now <= n ? now + n : now - n]]; 58 now <= n ? fail |= (id[now + n] == id[now]) : fail |= (id[now - n] == id[now]); 59 } 60 if (now == x) break; 61 } 62 used[colcnt] = flag; 63 } 64 } 65 int ans[maxn], tot; 66 int main() 67 { 68 n = F(); R int m = F(); 69 for (R int i = 1; i <= m; ++i) 70 { 71 R int a = F(), b = F(), w = F(); 72 link1(a, b, w); 73 } 74 R int ptot = 2 * n; 75 for (R int i = 1; i <= n; ++i) 76 { 77 // printf("i = %d\n", i); 78 R int lp = ptot, rp; 79 for (R edge *iter = lt[i]; iter; iter = iter -> next) 80 { 81 link(i + n, iter -> to + n * iter -> w); 82 ptot != lp ? link(ptot + 1, ptot), 1 : 0; 83 link(++ptot, iter -> to + n * iter -> w); 84 } 85 rp = ptot; 86 for (R edge *iter = lt[i]; iter; iter = iter -> next) 87 { 88 if (iter != lt[i]) link(ptot, ptot + 1); 89 link(++ptot, iter -> to + n * iter -> w); 90 } 91 for (R edge *iter = rt[i]; iter; iter = iter -> next) 92 if (!iter -> w) link(i + n, iter -> to); 93 R int counter = 0; 94 for (R edge *iter = lt[i]; iter; iter = iter -> next) 95 { 96 ++counter; 97 if (iter != lt[i]) link(iter -> to + n * (iter -> w ^ 1), lp + counter - 1); 98 if (iter -> next) link(iter -> to + n * (iter -> w ^ 1), rp + counter + 1); 99 // for (R edge *iter2 = lt[i]; iter2; iter2 = iter2 -> next) 100 // if (!(iter -> to == iter2 -> to && iter -> w == iter2 -> w)) 101 // { 102 // link(iter -> to + n * (iter -> w ^ 1), iter2 -> to + n * iter2 -> w); 103 // link(iter2 -> to + n * (iter2 -> w ^ 1), iter -> to + n * iter -> w); 104 // } 105 } 106 for (R edge *iter = rt[i]; iter; iter = iter -> next) 107 if (iter -> w) link(i, iter -> to); 108 } 109 for (R int i = 1; !fail && i <= n; ++i) if (!dfn[i]) tarjan(i, 0); 110 if (fail) 111 { 112 puts("Impossible"); 113 return 0; 114 } 115 for (R int i = 1; i <= n; ++i) if (used[id[i]]) ans[++tot] = i; 116 printf("%d\n", tot); 117 std::sort(ans + 1, ans + tot + 1); 118 for (R int i = 1; i <= tot; ++i) printf("%d ", ans[i]); 119 return 0; 120 }
以上是关于UOJ #210UER #6寻找罪犯的主要内容,如果未能解决你的问题,请参考以下文章
UOJ_14_UER #1DZY Loves Graph_并查集