P5782 [POI2001] 和平委员会

原题传送门

题目大意

根据宪法,Byteland 民主共和国的公众和平委员会应该在国会中通过立法程序来创立。 不幸的是,由于某些党派代表之间的不和睦而使得这件事存在障碍。 此委员会必须满足下列条件:

  • 每个党派都在委员会中恰有 11 个代表。
  • 如果 22 个代表彼此厌恶,则他们不能都属于委员会。

每个党在议会中有 22 个代表。代表从 11 编号到 2n2n。 编号为 2i12i-12i2i 的代表属于第 ii 个党派。

任务:写一程序读入党派的数量和关系不友好的代表对,计算决定建立和平委员会是否可能,若行,则列出委员会的成员表。

如果不能创立委员会,则输出信息 NIE

解题思路

比较裸的 2-SAT 问题。

2-SAT 都是指给你许多约束条件,类似于二选一之类的,让你输出任意一种合法解。

如果是 kk 选一,那就是 k-SAT 问题。

首先思考,如果是二选一的问题,那是不是这两个互相排斥的点不能连在一起?

解题流程:

  1. 对于所有两个互相排斥的点,就不能连边,将集合里没有明确关系的点就连一条边。

  2. 然后对每一个没有在上一遍 tarjan 遍历到的点都 tarjan 一遍。

  3. 判断是否有不可满足的解。

  4. 输出正确解。

建边: 举个例子,设有 AABB 两个党派,若 A1A_1B2B_2 互相厌恶,则 A1A_1B2B_2 无法连边,于是只能 A1A_1B1B_1 连边,A2A_2B2B_2 连边。

注意点的数量要改是 2n2n

若一个点的编号为 xx,则与他在同一个党派的另一个代表的计算方法则为:

int js(int x)
{
	return (x % 2) ? x + 1 : x - 1;
}

然后对每一个没有遍历到的点,都跑一边 tarjan

然后看第一个条件:

每个党派都在委员会中恰有 11 个代表。

只需判断同一党派的两个代表是否在同一个强连通分量。

for(int i = 1; i <= 2 * n; ++i)
{
	if(i % 2 == 1 && id[i] == id[i + 1])
	{
		printf("NIE\n");
		return 0;
	}
}

再看第二个条件:

如果 22 个代表彼此厌恶,则他们不能都属于委员会。

再刚刚建边的时候已经讨论过了,直接跳过。

最后输出部分,:

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;
}
posted @ 2021-08-02 21:48  蒟蒻orz  阅读(19)  评论(0编辑  收藏  举报  来源