123789456ye

已AFO

[NOI2017]游戏

题面:Luogu
题解:2-SAT
首先看起来很像3-SAT,然而众所周知3-SAT是np的
考虑到\(x\)图只有\(d\le 8\)个,所以可以直接枚举\(2^d\)\(x\)
为什么不是\(3^d\)?因为\(x\)取两个就够了,取第三个并没有什么用
比如取\(a\),你可以开\(b,c\)的车子;取\(b\),你可以开\(a,c\)的车子。如果这两种都不行那就是无解
以字典序在前的为\(i\),在后的为\(i'\)(学过2-SAT的应该看得懂)
也就是

\[s[i]=a,i=b,i'=c\\ s[i]=b,i=a,i'=c\\ s[i]=c,i=a,i'=b\]

对于一个规则(要求)\((i,h_i,j,h_j)\)有三种情况

  • \(i\)图不能跑\(h_i\)
    此时显然直接跳过

  • \(j\)图不能跑\(h_j\)
    换句话将就是\(i\)图不能选\(h_i\),必须选另一个
    那么直接连\((i,i')\)

  • \(else\)
    \((i,j),(j',i')\)

注意连边实现细节略多,代码里有少量注释

#include<bits/stdc++.h>
using namespace std;
inline void read(int& x)
{
	x = 0; char c = getchar();
	while (!isdigit(c)) c = getchar();
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar();
}
#define maxn 200005
int n, m, d;
namespace Graph
{
	struct Edge
	{
		int fr, to;
	}eg[maxn << 1];
	int head[maxn], edgenum;
	inline void add(int fr, int to)
	{
		eg[++edgenum] = { head[fr],to };
		head[fr] = edgenum;
	}
	int dfn[maxn], low[maxn], dfn_clock, col[maxn], colnum, vis[maxn];
	stack<int> sta;
#define to eg[i].to
	void paint(int rt)
	{
		vis[rt] = 0; sta.pop();
		col[rt] = colnum;
	}
	void Tarjan(int rt)
	{
		low[rt] = dfn[rt] = ++dfn_clock;
		vis[rt] = 1; sta.push(rt);
		for (int i = head[rt]; i; i = eg[i].fr)
		{
			if (!dfn[to]) Tarjan(to), low[rt] = min(low[rt], low[to]);
			else if (vis[to]) low[rt] = min(low[rt], dfn[to]);
		}
		if (low[rt] == dfn[rt])
		{
			++colnum;
			while (sta.top() != rt) paint(sta.top());
			paint(rt);
		}
	}
}
using namespace Graph;
int x[10], pos[10];
char s[maxn];
struct Rule { int i, j; char hi, hj; }r[maxn];
inline bool check()
{
	for (int i = 1; i <= (n << 1); ++i)
		if (!dfn[i]) Tarjan(i);
	for (int i = 1; i <= n; ++i)
		if (col[i] == col[i + n]) return false;
	return true;
}
void solve()
{
	for (int Set = 0; Set < (1 << d); ++Set)
	{
		edgenum = dfn_clock = colnum = 0;
		for (int i = 1; i <= (n << 1); ++i) col[i] = head[i] = dfn[i] = 0;
		for (int i = 1; i <= d; ++i) s[pos[i]] = (Set & (1 << (i - 1))) ? 'A' : 'B';
		for (int i = 1; i <= m; ++i)
		{
			if (r[i].hi == s[r[i].i]) continue;
			if (r[i].hj == s[r[i].j])
			{
				if (r[i].hi == 'C' || (r[i].hi == 'B' && s[r[i].i] == 'C')) add(r[i].i + n, r[i].i);
				//这个if里的东西如果为真,就不能选i'(在这个代码里是B或C),下面那个是一样的
				else add(r[i].i, r[i].i + n);
				continue;
			}
			int flag1 = 0, flag2 = 0;
			if (r[i].hi == 'C' || (r[i].hi == 'B' && s[r[i].i] == 'C')) flag1 = 1;
			if (r[i].hj == 'C' || (r[i].hj == 'B' && s[r[i].j] == 'C')) flag2 = 1;
			add(r[i].i + n * flag1, r[i].j + n * flag2);
			add(r[i].j + n * (flag2 ^ 1), r[i].i + n * (flag1 ^ 1));
		}
		if (check())
		{
			for (int i = 1; i <= n; ++i)
			{
				if (col[i] < col[n + i]) putchar((s[i] == 'A') ? 'B' : 'A');//A或B
				else putchar((s[i] == 'C') ? 'B' : 'C');//B或C
			}
			return;
		}
	}
	printf("-1");
}

int main()
{
	read(n), read(d), d = 0;
	scanf("%s", s + 1); read(m);
	for (int i = 1; i <= m; ++i) read(r[i].i), scanf("%c", &r[i].hi), read(r[i].j), scanf("%c", &r[i].hj);
	for (int i = 1; i <= n; ++i)
	{
		s[i] -= 32;
		if (s[i] == 'X') pos[++d] = i;
	}
	solve();
	return 0;
}
posted @ 2020-03-22 17:37  123789456ye  阅读(154)  评论(0编辑  收藏  举报