题目链接:点我啊╭(╯^╰)╮
题目大意:
n
n
n 个嫌疑人,
m
m
m 条供词,两种供词:
x
i
x_i
xi 说
y
i
y_i
yi 是犯人,
x
i
x_i
xi 说
y
i
y_i
yi 不是犯人。
注意有限制:
每一个犯人的所有供词最多有一句是假的
不是犯人的嫌疑人的供词总是真的
找出所有犯人,提供任意一组可行解
解题思路:
很明显可以用
2
−
s
a
t
2-sat
2−sat 解决
但是如果枚举一个人可能说的假话,总共的边数是
m
2
m^2
m2 的
所以考虑优化建边,注意到这里如果有一句假话,则前面和后面都是真话
因此可以用前缀来优化建边(说出来我可能也建不出来。。。)
设
1
−
n
1-n
1−n 表示第
i
i
i 个人不是犯人,
n
+
1
−
2
n
n+1 - 2n
n+1−2n 表示第
i
i
i 个人是犯人
2
n
+
1
−
2
n
+
m
2n + 1 - 2n + m
2n+1−2n+m 表示第
i
i
i 句话以及以前都是真话
2
n
+
m
+
1
−
2
n
+
2
m
2n + m + 1 - 2n + 2m
2n+m+1−2n+2m 表示第
i
i
i 句话以及以前都是假话
然后就可以讨论每一句话是否是假话了
注意最后还要讨论这个人是否是犯人与讲话的情况
这谁想的全啊
#include<bits/stdc++.h>
#define rint register int
#define deb(x) cerr<<#x<<" = "<<(x)<<'\n';
using namespace std;
typedef long long ll;
typedef pair <int,int> pii;
const int maxn = 4e5 + 5;
int n, m, pre[maxn], ans[maxn], cnt;
int dfn[maxn], low[maxn], tot, numc;
int st[maxn], top, vis[maxn], col[maxn];
vector <int> g[maxn];
inline int check(int a, int b){return a+b*n;}
inline int work(int a, int b){return n*2+a+b*m;}
inline void add(int a, int b){g[a].push_back(b);}
void tarjan(int u){
dfn[u] = low[u] = ++tot;
st[++top] = u;
vis[u] = 1;
for(int i=0; i<g[u].size(); i++){
int v = g[u][i];
if(!dfn[v]){
tarjan(v);
low[u] = min(low[u], low[v]);
} else if(vis[v]) low[u] = min(low[u], dfn[v]);
}
if(dfn[u] == low[u]){
++numc;
while(st[top+1] ^ u){
col[st[top]] = numc;
vis[st[top--]] = 0;
}
}
}
signed main() {
scanf("%d%d", &n, &m);
for(int i=1; i<=n; i++) pre[i] = m * 2 + 1;
for(int i=1, u, v, f; i<=m; i++){
scanf("%d%d%d", &u, &v, &f); f ^= 1;
add(check(v, f^1), work(pre[u], 0));
// 若这句话是假话,则以前都是真话
add(work(pre[u], 1), check(v, f));
// 若上句话是假话,则这句话是真话
add(work(i, 0), check(v, f));
// 若这句话以及之前都是真话 ,则这句话是真话
add(check(v, f^1), work(i, 1));
// 若这句话是假话,则这句话以及以前有假话
add(work(i, 0), work(pre[u], 0));
// 若这句话以及以前都是真话,则这句话以前都是真话
add(work(pre[u], 1), work(i, 1));
// 若这句话以前有假话,则这句话以及以前有假话
pre[u] = i; // 更新 u的上一句话
}
for(int i=1; i<=n; i++){
add(work(pre[i], 1), check(i, 1));
// 若这个人以前说过假话,这个人必死
add(check(i, 0), work(pre[i], 0));
// 若这个人很诚实,则之前的话都是真的
}
for(int i=1; i<=(n+m)<<1; i++)
if(!dfn[i]) tarjan(i);
for(int i=1; i<=n; i++)
if(col[i] == col[i+n]){
puts("Impossible");
return 0;
} else if(col[i] > col[i+n]) ans[++cnt] = i;
printf("%d\n", cnt);
for(int i=1; i<=cnt; i++) printf("%d ", ans[i]);
}