[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;
}
一切伟大的行动和思想,都有一个微不足道的开始。
There is a negligible beginning in all great action and thought.
There is a negligible beginning in all great action and thought.