P5782 [POI2001] 和平委员会
题目
思路
因为每个党只有 2 个代表,所以这个题目很好做。
考虑使用 2-SAT,如果这个党的 \(x\) 厌恶另外一个党的 \(y\),那么如果 \(x\) 存在,那么 \(y\) 不能参加,而一个党必须有一个人,所以只能由 \(y\) 的同伙出席,反之亦然。
这已具备 2-SAT 题目特征,连边即可。
无解的情况为:一个党的两个人在一个强连通分量里。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 2000010;
struct edge {
int to, next;
} e[N];
int head[N], idx = 1;
void add(int u, int v) {
idx++, e[idx].to = v, e[idx].next = head[u], head[u] = idx;
}
int n, m;
int dfn[N], low[N], cnt;
int stk[N], top;
bool instk[N];
int scc[N], tot;
void tarjan(int u) {
stk[++top] = u, instk[u] = 1;
dfn[u] = low[u] = ++cnt;
for (int i = head[u]; i; i = e[i].next) {
int to = e[i].to;
if (!dfn[to]) {
tarjan(to);
low[u] = min(low[u], low[to]);
}
else if (instk[to]) low[u] = min(low[u], dfn[to]);
}
if (dfn[u] == low[u]) {
tot++;
while (stk[top] != u) {
int t = stk[top];
top--;
instk[t] = 0;
scc[t] = tot;
}
top--;
instk[u] = 0;
scc[u] = tot;
}
}
int turn(int x) {
if (x & 1) return x + 1;
else return x - 1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n >> m;
for (int i = 1; i <= m; i++) {
int p1, p2;
cin >> p1 >> p2;
add(p1, turn(p2));
add(p2, turn(p1));
}
for (int i = 1; i <= 2 * n; i++) if (!dfn[i]) tarjan(i);
for (int i = 1; i <= 2 * n; i += 2) {
if (scc[i] == scc[i + 1]) {
cout << "NIE\n";
return 0;
}
}
for (int i = 1; i <= 2 * n; i += 2) {
if (scc[i + 1] < scc[i]) cout << i + 1 << '\n';
else cout << i << '\n';
}
return 0;
}