「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;
}
posted @ 2020-06-05 20:31  Sangber  阅读(239)  评论(0编辑  收藏  举报