Description
有 (n) 次比赛,每次比赛类别是 (x, a, b, c)。
每次从小 (L) 所有的赛车中 (A, B, C) 三选一参加比赛。
(A) 赛车不能在 (a),(B) 赛车不能在 (b),(C) 赛车不能在 (c)。
还有一些约束条件:
在 (i) 场使用型号 (x),在 (j) 场必须使用 (y)。
问是否存在合法解,如果存在随便输出一组方案。
还有一个条件是有 (d) 个 (x)。
Solution
发现 (a) 只能匹配 (B, C),(b) 只能匹配 (A, C),(c) 只能匹配 (A, B),显然这三种是一个 ( ext{2-SAT})。
考虑 (d = 0) 的情况怎么做,即对于约束条件 (i, x, j, y),如何处理:
-
若 (i) 不能用 (x) 车,那么这个条件莫须有(因为 (i) 永远不会选 (x) 车),不管了
-
若 (j) 不能用 (y) 车,那么 (i) 也不能用 (x) 车,所以直接让 (i, x) 这个条件矛盾即可,即让 (i) 连边 (i')。
-
否则即均符合题意要求,那么直接连边,另外还要注意加入逆否命题!
但是 (x) 有三种取值,( ext{3-SAT}) 是 (NPC) 问题没法搞啊...一看数据范围, (d le 8)。(3 ^ 8) 暴力会爆炸过不了 (n le 50000)。考虑到暴力枚举的方法太绝对了,相当于 ( ext{3-SAT} Rightarrow ext{1-SAT})。
不妨让其变成 ( ext{2-SAT}) 变换一下 (QAQ),对于每个 (x):
- 强制不选 (A),即只能选 (BC)
- 强制不选 (B),即只能选 (AC)
考虑到一个合法方案中 (x) 的取值是唯一的,所以如果存在解,必然能可以找到解。(尝试当 (x) 在合法解中是任意形式,总会在某种情况被枚举到)
所以这样我们每个就枚举两种情况就行了,然后按照 (d = 0) 来做...
复杂度
(O(2 ^ d(n + m)))
Code
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 50005, M = 100005;
int n, m, d, c[10], tot, head[N << 1], numE;
char s[N];
int A[M], B[M], X[M], Y[M], st[N], top;
int dfn[N << 1], low[N << 1], dfncnt, col[N << 1], cnt;
bool ins[N << 1];
struct E{
int next, v;
} e[M << 1];
void inline add(int u, int v) {
e[++numE] = (E) { head[u], v };
head[u] = numE;
}
void inline clear() {
numE = top = cnt = dfncnt = 0;
memset(head, 0, sizeof head);
memset(dfn, 0, sizeof head);
memset(ins, false, sizeof ins);
memset(col, 0, sizeof 0);
}
int inline get(int i, int j) {
if (s[i] == 'a') return i + n * (j - 1);
else if (s[i] == 'b') return i + n * (j == 0 ? 0 : 1);
else return i + n * j;
}
int inline opp(int x) {
return x <= n ? x + n : x - n;
}
bool inline check() {
for (int i = 1; i <= n; i++)
if (col[i] == col[i + n]) return false;
return true;
}
int inline change(int i, int j) {
if (s[i] == 'a') return j + 1;
else if (s[i] == 'b') return j == 0 ? 0 : 2;
else return j;
}
void tarjan(int u) {
dfn[u] = low[u] = ++dfncnt;
ins[u] = true, st[++top] = u;
for (int i = head[u]; i; i = e[i].next) {
int v = e[i].v;
if (!dfn[v]) tarjan(v), low[u] = min(low[u], low[v]);
else if (ins[v]) low[u] = min(low[u], dfn[v]);
}
if (low[u] == dfn[u]) {
int v; ++cnt;
do {
v = st[top--], ins[v] = false, col[v] = cnt;
} while (v != u);
}
}
bool inline work() {
clear();
for (int t = 1; t <= m; t++) {
int i = A[t], x = X[t], j = B[t], y = Y[t];
int a = get(i, x), b = get(j, y);
if (s[i] - 'a' == x) continue;
else if (s[j] - 'a' == y) add(a, opp(a));
else add(a, b), add(opp(b), opp(a));
}
for (int i = 1; i <= 2 * n; i++)
if (!dfn[i]) tarjan(i);
if (check()) {
for (int i = 1; i <= n; i++)
putchar('A' + (col[i] < col[i + n] ? change(i, 0) : change(i, 1)));
return true;
}
return false;
}
int main() {
scanf("%d%d%s%d", &n, &d, s + 1, &m);
for (int i = 1; i <= n; i++) if (s[i] == 'x') c[tot++] = i;
for (int i = 1; i <= m; i++) {
char x[2], y[2];
scanf("%d%s%d%s", A + i, x, B + i, y);
X[i] = x[0] - 'A'; Y[i] = y[0] - 'A';
}
for (int i = 0; i < (1 << d); i++) {
for (int j = 0; j < d; j++) s[c[j]] = (i >> j & 1) ? 'b' : 'a';
if (work()) return 0;
}
printf("-1");
return 0;
}