題目鏈接:點我啊╭(╯^╰)╮
題目大意:
個嫌疑人, 條供詞,兩種供詞:
說 是犯人, 說 不是犯人。
注意有限制:
每一個犯人的所有供詞最多有一句是假的
不是犯人的嫌疑人的供詞總是真的
找出所有犯人,提供任意一組可行解
解題思路:
很明顯可以用 解決
但是如果枚舉一個人可能說的假話,總共的邊數是 的
所以考慮優化建邊,注意到這裏如果有一句假話,則前面和後面都是真話
因此可以用前綴來優化建邊(說出來我可能也建不出來。。。)
設 表示第 個人不是犯人, 表示第 個人是犯人
表示第 句話以及以前都是真話
表示第 句話以及以前都是假話
然後就可以討論每一句話是否是假話了
注意最後還要討論這個人是否是犯人與講話的情況
這誰想的全啊
#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]);
}