【UOJ #210】【UER #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 表示犯人的数目。

第二行 个整数 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
", 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
", 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
", 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
", 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 }
原文地址:https://www.cnblogs.com/cocottt/p/6780920.html