和平委员会
题目链接:ybt金牌导航3-6-4 / luogu P5782
题目大意
有一些人,然后 2i-1 和 2i 编号的人是同一个组别的,一定要也只能出现其中一个。
然后还有一些限制条件说某两个人不能同时出现。
问你是否有一种出现方案,使得所有条件都被满足。
如果有输出任意一组(把选的人输出出来),否则输出无解。
思路
这道题很明显看出是 2-set。
然后那同一个组别的就看做是点和反向点。
然后不能出现就是选 A 就要选 B 反向点(连边),选 B 就要选 A 反向点(连边)。
然后剩下的就是 2-set 操作了。
代码
#include<cstdio>
#include<iostream>
using namespace std;
struct node {
int to, nxt;
}e[40001];
int n, m, x, y, tot_num, sta[20001], tmp, tot;
int le[20001], KK, dfn[20001], low[20001];
int in[20001];
int another(int x) {
if (x & 1) return x + 1;
return x - 1;
}
void add(int x, int y) {
e[++KK] = (node){y, le[x]}; le[x] = KK;
}
void tarjan(int now) {
dfn[now] = low[now] = ++tmp;
sta[++sta[0]] = now;
for (int i = le[now]; i; i = e[i].nxt)
if (!dfn[e[i].to]) {
tarjan(e[i].to);
low[now] = min(low[now], low[e[i].to]);
}
else if (!in[e[i].to]) low[now] = min(low[now], low[e[i].to]);
if (dfn[now] == low[now]) {
in[now] = ++tot;
while (sta[sta[0]] != now) {
in[sta[sta[0]]] = tot;
sta[0]--;
}
sta[0]--;
}
}
int main() {
scanf("%d %d", &n, &m);
tot_num = n + n;
for (int i = 1; i <= m; i++) {
scanf("%d %d", &x, &y);
add(x, another(y));//建图
add(y, another(x));
}
for (int i = 1; i <= tot_num; i++)
if (!dfn[i]) tarjan(i);
for (int i = 1; i <= tot_num; i += 2)
if (in[i] == in[another(i)]) {
printf("NIE");
return 0;
}
for (int i = 1; i <= tot_num; i += 2) {//输出方案(选逆拓扑序小的,也就是拓扑序大的)
printf("%d\n", (in[i] < in[another(i)]) ? i : another(i));
}
return 0;
}