1. 程式人生 > 實用技巧 >【UOJ #210】【UER #6】尋找罪犯

【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