zoukankan      html  css  js  c++  java
  • 【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 }
  • 相关阅读:
    postman设置页面详解
    postman安装使用
    测试入门1:黑盒测试用例设计方法
    oo第十六次作业
    oo第三单元总结
    OO第二单元总结
    select语句
    MySQL数据库基础操作
    创建和查看数据库
    认识MySQL数据库
  • 原文地址:https://www.cnblogs.com/cocottt/p/6780920.html
Copyright © 2011-2022 走看看