P5782 [POI2001] 和平委员会
题目大意
根据宪法,Byteland 民主共和国的公众和平委员会应该在国会中通过立法程序来创立。 不幸的是,由于某些党派代表之间的不和睦而使得这件事存在障碍。 此委员会必须满足下列条件:
- 每个党派都在委员会中恰有 个代表。
- 如果 个代表彼此厌恶,则他们不能都属于委员会。
每个党在议会中有 个代表。代表从 编号到 。 编号为 和 的代表属于第 个党派。
任务:写一程序读入党派的数量和关系不友好的代表对,计算决定建立和平委员会是否可能,若行,则列出委员会的成员表。
如果不能创立委员会,则输出信息 NIE
。
解题思路
比较裸的 2-SAT
问题。
2-SAT
都是指给你许多约束条件,类似于二选一之类的,让你输出任意一种合法解。
如果是 选一,那就是 k-SAT
问题。
首先思考,如果是二选一的问题,那是不是这两个互相排斥的点不能连在一起?
解题流程:
-
对于所有两个互相排斥的点,就不能连边,将集合里没有明确关系的点就连一条边。
-
然后对每一个没有在上一遍
tarjan
遍历到的点都tarjan
一遍。 -
判断是否有不可满足的解。
-
输出正确解。
建边: 举个例子,设有 和 两个党派,若 与 互相厌恶,则 和 无法连边,于是只能 和 连边, 和 连边。
注意点的数量要改是 。
若一个点的编号为 ,则与他在同一个党派的另一个代表的计算方法则为:
int js(int x)
{
return (x % 2) ? x + 1 : x - 1;
}
然后对每一个没有遍历到的点,都跑一边 tarjan
。
然后看第一个条件:
每个党派都在委员会中恰有 个代表。
只需判断同一党派的两个代表是否在同一个强连通分量。
for(int i = 1; i <= 2 * n; ++i)
{
if(i % 2 == 1 && id[i] == id[i + 1])
{
printf("NIE\n");
return 0;
}
}
再看第二个条件:
如果 个代表彼此厌恶,则他们不能都属于委员会。
再刚刚建边的时候已经讨论过了,直接跳过。
最后输出部分,:
for(int i = 1; i <= 2 * n; ++i)
{
if(id[i] < id[js(i)])
{
cout << i << endl;
}
}
Special Judge:如果委员会能以多种方法形成,程序可以只输出它们的某一个。
注意这里是小于,如果直接写等于,就会重复输出。
AC CODE
由于笔者快读太长,不方便放在这里,请读者不要复制本代码。
#include <bits/stdc++.h>
using namespace std;
#define _ 20005
int n, m, cnt_node, cntn;
int ans[2000005];
int cnt;
array<int, 2000005> head;
struct abc
{
int to, nxt;
};
array<abc, 2000005> dd;
array<bool, 2000005> vis;
array<int, 2000005> dfn, low, id;
stack<int> s;
inline void add(int u, int v)
{
dd[++cnt].to = v;
dd[cnt].nxt = head[u];
head[u] = cnt;
}
inline void tarjan(int u)
{
dfn[u] = low[u] = ++cnt_node;
s.push(u);
vis[u] = 1;
for (int e = head[u]; e; e = dd[e].nxt)
{
if (!dfn[dd[e].to])
{
tarjan(dd[e].to);
low[u] = min(low[dd[e].to], low[u]);
}
else if (vis[dd[e].to])
low[u] = min(low[u], dfn[dd[e].to]);
}
if (low[u] == dfn[u])
{
cntn++;
while (1)
{
int now = s.top();
s.pop();
vis[now] = 0;
id[now] = cntn;
if (now == u)
break;
}
}
}
int js(int x)
{
return (x % 2) ? x + 1 : x - 1;
}
signed main()
{
cin >> n >> m;
for(int i = 1; i <= m; ++i)
{
int a, b;
cin >> a >> b;
add(a, js(b));
add(b, js(a));
}
for(int i = 1; i <= 2 * n; ++i)
if(!dfn[i]) tarjan(i);
for(int i = 1; i <= 2 * n; ++i)
{
if(i % 2 == 1 && id[i] == id[i + 1])
{
printf("NIE\n");
return 0;
}
}
for(int i = 1; i <= 2 * n; ++i)
{
if(id[i] < id[js(i)])
{
cout << i << endl;
}
}
return 0;
}
本文来自博客园,作者:蒟蒻orz,转载请注明原文链接:https://www.cnblogs.com/orzz/p/18122158
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!