【UOJ #210】【UER #6】寻找罪犯

题目描述

有n个人分为好人和坏人,说了m句话。好人不会说假话,坏人至多说一句谎话。求出一组解,满足要求。

题解

利用2-SAT拆点,一个人拆成两个点,表示他是好人和坏人。然而这样的话边数是m^2的,所以用前/后缀和优化构图即可。

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 
  4 const int N = 1e6 + 10;
  5 const int M = 3e6 + 10;
  6 inline int read()
  7 {
  8     char ch = getchar();int x = 0, f = 1;
  9     while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
 10     while(ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) - '0' + ch; ch = getchar();}
 11     return x * f;
 12 }
 13 int n, m;
 14 int h[N], e[M], ne[M], idx;
 15 int dfn[N], low[N], num, in[N], stac[N], top, pre[N];
 16 int c[N], cnt;
 17 
 18 void add(int a, int b)
 19 {
 20     e[idx] = b;
 21     ne[idx] = h[a];
 22     h[a] = idx++;
 23 }
 24 
 25 int crm(int x, int f)
 26 {
 27     return x + n * f;
 28 }
 29 
 30 int word(int x, int f)
 31 {
 32     return n + n + x + m * f;
 33 }
 34 
 35 void tarjan(int u)
 36 {
 37     stac[++ top] = u;
 38     dfn[u] = low[u] = ++ num;
 39     in[u] = 1;
 40     for (int i = h[u]; ~i; i = ne[i])
 41     {
 42         int j = e[i];
 43         if(!dfn[j])
 44         {
 45             tarjan(j);
 46             low[u] = min(low[u], low[j]);
 47         }
 48         else if(in[j])
 49             low[u] = min(low[u], dfn[j]);
 50     }
 51     if(low[u] == dfn[u])
 52     {
 53         int z;
 54         cnt ++;
 55         do{
 56             z = stac[top --];
 57             c[z] = cnt;
 58             in[z] = 0;
 59         }while(z != u);
 60     }
 61 }
 62 
 63 
 64 int main()
 65 {
 66     memset(h, -1, sizeof(h));
 67     n = read(), m = read();
 68     for (int i = 1; i <= n; i ++) pre[i] = 2*m + 1;
 69     for (int i = 1; i <= m; i ++)
 70     {
 71         int x = read(), y = read(), t = read();
 72         t ^= 1;
 73         add(crm(y, t ^ 1), word(pre[x], 0)); //若这次y的身份与x说的相反,则x前面说的话都对。
 74         
 75         add(word(pre[x], 1), crm(y, t));    //若x前面说过谎话了,y的身份与x说的相符
 76 
 77         add(word(i, 0), crm(y, t));         //若说的话一直是真的,y的身份与x相符
 78         
 79         add(crm(y, t ^ 1), word(i, 1));     //若y的身份与x说的不符,x这句话是假的
 80         
 81         add(word(pre[x], 1), word(i, 1));   //前面说过假话,现在也保持说过假话的状态
 82         
 83         add(word(i, 0), word(pre[x], 0));   //现在都没说过假话,前面也没说过假话
 84         pre[x] = i;                         //更新前缀
 85     }
 86     for (int i = 1; i <= n; i ++)
 87     {
 88         add(word(pre[i], 1), crm(i, 1));    //说过假话,一定是坏人
 89         add(crm(i, 0), word(pre[i], 0));    //好人一定没说过假话
 90     }
 91     for (int i = 1; i <= (n + m) * 2; i ++)
 92             if(!dfn[i])
 93                 tarjan(i);
 94     bool flag = true;
 95     for (int i = 1; i <= n; i ++)
 96     {
 97         if(c[i] == c[i + n])
 98         {
 99             flag = false;
100             break;
101         }
102     }
103     for (int i = 1; i <= m; i ++)
104     {
105         if(c[n + n + i] == c[n + n + m + i])
106         {
107             flag = false;
108             break;
109         }
110     }
111     if(!flag)
112     {
113         puts("Impossible");
114     }
115     else
116     {
117         vector<int>ans;
118         for (int i = 1; i <= n; i ++)
119         {
120             if(c[i] > c[i + n])
121                 ans.push_back(i);
122         }
123         cout << ans.size() << '\n';
124         for (int i = 0; i < ans.size(); i ++)
125         {
126             printf("%d%c", ans[i], i == ans.size() -1?'\n' : ' ');
127         }
128     }
129 }
View Code

 

posted @ 2020-11-24 19:15  逾期不候丶  阅读(168)  评论(0编辑  收藏  举报