【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 }