「NOI2017」游戏
不难发现题目可以转化成 \(\text{2-SAT}\) 模型。
对于 \(\text{A、B、C}\) 三种场地,它们都只有两种可能的场地选择,麻烦的是 \(\text{X}\) 场地,我们发现它有三种选择,而我们是不可能做 \(\text{3-SAT}\) 的(这根本做不了嘛。
对于这种情况我们一般是先 \(\text{DFS}\) ,把三种选择变成两种选择,然后再一起跑 \(\text{2-SAT}\) 。
我们发现 \(\text{X}\) 场地的数量最多是 \(8\) ,那么我们就可以直接枚举每一个 \(\text{X}\) ,把他分别作为 \(\text{A、B、C}\) 然后根据限制条件建图跑 \(\text{2-SAT}\),连边方式和输出方案应该都比较好想,就不多说了(可以结合代码理解)。
但是我们发现 \(3^8=6561\) ,再乘一个 \(10^5\) 级别的 \(n\) ,这显然是过不了的。
怎么办呢?我们在 \(\text{DFS}\) 时其实可以做到 \(2^8\),我们只需要枚举 \(\text{X} \to \text{A},\text{X} \to \text{B}\) 两种,因为这样就可以覆盖 \(\text{X}\) 放三种不同赛车的情况了。
参考代码:
#include <algorithm>
#include <cstdlib>
#include <cstring>
#include <cstdio>
using namespace std;
template < class T > void read(T& s) {
s = 0; int f = 0; char c = getchar();
while ('0' > c || c > '9') f |= c == '-', c = getchar();
while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
s = f ? -s : s;
}
const int _ = 2e5 + 5;
int tot, head[_]; struct Edge { int v, nxt; } edge[_];
void Add_edge(int u, int v) { edge[++tot] = (Edge) { v, head[u] }, head[u] = tot; }
int n, d, m, a[_], ch[2][_], id[10];
struct node { int u, hu, v, hv; } t[_];
int num, dfn[_], low[_], col, co[_], top, stk[_];
void tarjan(int u) {
dfn[u] = low[u] = ++num, stk[++top] = u;
for (int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].v;
if (!dfn[v])
tarjan(v), low[u] = min(low[u], low[v]);
else
if (!co[v]) low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
++col;
do co[stk[top]] = col;
while (stk[top--] != u);
}
}
void init() {
tot = num = col = top = 0;
memset(head, 0, sizeof head);
memset(dfn, 0, sizeof dfn);
memset(co, 0, sizeof co);
}
void solve() {
init();
for (int i = 1; i <= m; ++i) {
int u = t[i].u, hu = t[i].u + t[i].hu * n;
int v = t[i].v, hv = t[i].v + t[i].hv * n;
if (a[u] == t[i].hu) continue ;
if (a[v] == t[i].hv)
Add_edge(ch[ch[1][u] == hu][u], ch[ch[1][u] != hu][u]);
else {
Add_edge(hu, hv);
Add_edge(ch[ch[1][v] != hv][v], ch[ch[1][u] != hu][u]);
}
}
for (int i = 1; i <= n; ++i) {
if (!dfn[ch[0][i]]) tarjan(ch[0][i]);
if (!dfn[ch[1][i]]) tarjan(ch[1][i]);
}
for (int i = 1; i <= n; ++i)
if (co[ch[0][i]] == co[ch[1][i]]) return ;
for (int i = 1; i <= n; ++i) {
if (co[ch[0][i]] < co[ch[1][i]])
printf("%c", (ch[0][i] - i) / n + 'A');
else
printf("%c", (ch[1][i] - i) / n + 'A');
}
exit(0);
}
void dfs(int x) {
if (x == d + 1) return solve();
int i = id[x];
a[i] = 0, ch[0][i] = i + n, ch[1][i] = i + 2 * n, dfs(x + 1);
a[i] = 1, ch[0][i] = i, ch[1][i] = i + 2 * n, dfs(x + 1);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("cpp.in", "r", stdin), freopen("cpp.out", "w", stdout);
#endif
read(n), read(d);
char S[_]; scanf("%s", S + 1);
for (int i = 1; i <= n; ++i) {
if (S[i] == 'a') a[i] = 0, ch[0][i] = i + n, ch[1][i] = i + 2 * n;
if (S[i] == 'b') a[i] = 1, ch[0][i] = i, ch[1][i] = i + 2 * n;
if (S[i] == 'c') a[i] = 2, ch[0][i] = i, ch[1][i] = i + n;
if (S[i] == 'x') id[++id[0]] = i;
}
read(m);
for (int u, hu, v, hv, i = 1; i <= m; ++i) {
read(u), hu = getchar() - 'A';
read(v), hv = getchar() - 'A';
t[i] = (node) { u, hu, v, hv };
}
dfs(1);
printf("-1");
return 0;
}