题面: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 n≤5×104, m ≤ 1 0 5 m \le 10^5 m≤105,无限制场数 ≤ 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' u→u′,表示第 i i i场不能使用 h i h_i hi
如果两场都能使用,那么连 u → v u \to v u→v, v ′ → u ′ v' \to u' v′→u′。对应如果第 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 ;
}