Luogu3825 NOI2017 游戏

9 篇文章 0 订阅
3 篇文章 0 订阅

题面luogu3825

题意 n n n场比赛,三种车型 A , B , C A, B, C A,B,C。每场比赛规定不能使用一种车型或是无限制,另有 m m m条限制如果第 i i i场使用 h i h_i hi车型,那么第 j j j场必须使用 h j h_j hj车型。输出一个比赛车型的合法方案,或 − 1 -1 1 n ≤ 5 × 1 0 4 n \le 5 \times 10^4 n5×104 m ≤ 1 0 5 m \le 10^5 m105,无限制场数 ≤ 8 \le 8 8

题解:首先假设所有的比赛都有限制车型。

那么是一个典型的2-SAT模型:每场比赛必须在两辆车中间选一辆,对应状态 i i i i ′ i' i

考虑 m m m条限制:

设第 i i i场使用车型 h i h_i hi对应状态 u u u,第 j j j场使用车型 h j h_j hj对应状态 v v v

如果第 i i i场不能使用车型 h i h_i hi,那么直接跳过;

如果第 i i i场能使用 h i h_i hi,第 j j j场不能使用 h j h_j hj,那么连 u → u ′ u \to u' uu,表示第 i i i场不能使用 h i h_i hi

如果两场都能使用,那么连 u → v u \to v uv v ′ → u ′ v' \to u' vu。对应如果第 i i i场使用 h i h_i hi车型,那么第 j j j场必须使用 h j h_j hj,和它的逆否命题。

跑2-SAT即可。构造方案时, i i i对应的选择即为 c [ i ] > c [ i + n ] c[i] > c[i + n] c[i]>c[i+n]。若 c [ i ] > c [ i + n ] c[i] > c[i + n] c[i]>c[i+n],选择 i ′ i' i;否则选择 i i i

#include <bits/stdc++.h>
using namespace std ;
inline int read() {
    int x = 0, f = 1; char c; c = getchar() ;
    while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
    return x * f ;
}
const int maxn = 5e4 + 10, maxm = 1e5 + 10 ;
int n, m, d ;
char str[maxn] ;
int head[2 * maxn], to[2 * maxm], nxt[2 * maxm], tot ;
int dfn[2 * maxn], low[2 * maxn], st[2 * maxn], c[2 * maxn], num, top, cnt ;
bool ins[2 * maxn] ;
inline void addEdge (int u, int v) {
    //printf("%d %d\n", u, v) ;
    to[++ tot] = v; nxt[tot] = head[u]; head[u] = tot ;
}
void tarjan (int v) {
    dfn[v] = low[v] = ++ num ;
    st[++ top] = v; ins[v] = 1 ;
    for (int i = head[v]; i; i = nxt[i]) {
        if (!dfn[to[i]]) {
            tarjan (to[i]) ;
            low[v] = min (low[v], low[to[i]]) ;
        } else if (ins[to[i]])
            low[v] = min (low[v], dfn[to[i]]) ;
    }
    if (dfn[v] == low[v]) {
        cnt ++; int y ;
        do {
            y = st[top --]; ins[y] = 0 ;
            c[y] = cnt ;
        }while (y != v) ;
    }
}
void init () {
    memset (head, 0, sizeof head) ;
    memset (dfn, 0, sizeof dfn) ;
    memset (ins, 0, sizeof ins) ;
    tot = num = top = cnt = 0 ;
}
int limi[maxm], limj[maxm] ;
char limhi[maxm][2], limhj[maxm][2] ;
int x[10] ;
int main() {
    //freopen("1.in", "r", stdin) ;
    //freopen("1.out", "w", stdout) ;
    scanf("%d%d%s%d", &n, &d, str + 1, &m) ;
    for (int i = 1; i <= m; i ++) {
        limi[i] = read(); scanf("%s", limhi[i]) ;
        limj[i] = read(); scanf("%s", limhj[i]) ;
    }
    int cnt = 0 ;
    for (int i = 1; i <= n; i ++)
        if (str[i] == 'x') x[++ cnt] = i ;
    for (int s = 0; s < (1 << d); s ++) {
        for (int i = 1; i <= cnt; i ++)
            str[x[i]] = (s & (1 << (i - 1))) ? 'b' : 'a' ;
        for (int i = 1; i <= m; i ++) {
            int tmp[3], cnt = 0 ;
            for (int j = 0; j < 3; j ++) {
                if (str[limi[i]] - 'a' == j) continue ;
                tmp[j] = cnt ++ ;
            }
            int x = tmp[limhi[i][0] - 'A'] ;
            cnt = 0 ;
            for (int j = 0; j < 3; j ++) {
                if (str[limj[i]] - 'a' == j) continue ;
                tmp[j] = cnt ++ ;
            }
            int y = tmp[limhj[i][0] - 'A'] ;
            if (str[limi[i]] - 'a' == limhi[i][0] - 'A') continue ;
            if (str[limj[i]] - 'a' == limhj[i][0] - 'A') {
                addEdge (limi[i] + n * x, limi[i] + n * (1 - x)) ;
            } else {
                addEdge (limi[i] + n * x, limj[i] + n * y) ;
                addEdge (limj[i] + (1 - y) * n, limi[i] + (1 - x) * n) ;
            }
        }
        for (int i = 1; i <= 2 * n; i ++)
            if (!dfn[i]) tarjan (i) ;
        bool flag = 1 ;
        for (int i = 1; i <= n; i ++)
            if (c[i] == c[i + n]) {
                flag = 0; break ;
            }
        if (!flag) {
            init (); continue ;
        }
        for (int i = 1; i <= n; i ++) {
            char tmp[3]; int cnt = 0 ;
            for (int j = 0; j < 3; j ++) {
                if (str[i] - 'a' == j) continue ;
                tmp[cnt ++] = 'A' + j ;
            }
            putchar (tmp[c[i] > c[i + n]]) ;
        }
        printf("\n") ;
        return 0 ;
    }
    printf("-1\n") ;
    return 0 ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值