@loj - 2305@ 「NOI2017」游戏
@description@
小 L 计划进行 n 场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。
小 L 的赛车有三辆,分别用大写字母 A、B、C 表示。地图一共有四种,分别用小写字母 x、a、b、c 表示。
其中,赛车 A 不适合在地图 a 上使用,赛车 B 不适合在地图 b 上使用,赛车 C 不适合在地图 c 上使用,而地图 x 则适合所有赛车参加。
适合所有赛车参加的地图并不多见,最多只会有 d 张。
n 场游戏的地图可以用一个小写字母组成的字符串描述。例如:S = xaabxcbc 表示小L计划进行 8 场游戏,其中第 1 场和第 5 场的地图类型是 x,适合所有赛车,第 2 场和第 3 场的地图是 a,不适合赛车 A,第 4 场和第 7 场的地图是 b,不适合赛车 B,第 6 场和第 8 场的地图是 c,不适合赛车 C。
小 L 对游戏有一些特殊的要求,这些要求可以用四元组 (i, hi, j, hj) 来描述,表示若在第 i 场使用型号为 hi 的车子,则第 j 场游戏要使用型号为 hj 的车子。
你能帮小 L 选择每场游戏使用的赛车吗?如果有多种方案,输出任意一种方案。
如果无解,输出 -1。
输入格式
输入第一行包含两个非负整数 n, d。输入第二行为一个字符串 S。
n, d, S 的含义见题目描述,其中 S 包含 n 个字符,且其中恰好 d 个为小写字母 x。
输入第三行为一个正整数 m,表示有 m 条用车规则。
接下来 m 行,每行包含一个四元组 i, hi, j, hj,其中 i, j 为整数,hi, hj 为字符 A、B 或 C,含义见题目描述。
输出格式
输出一行。
若无解输出 -1。
样例
样例输入
3 1
xcc
1
1 A 2 B
样例输出
ABA
数据范围与提示
n <= 5*10^4, d <= 8, m <= 10^5。
@solution@
“选了某个东西就必须要选另一个东西” 这样的二元依赖关系,不难想到使用 2-sat。
假如不考虑那 d 个能取 A, B, C 的变量,剩下的变量恰好从两个状态中选(A+B 或 B+C 或 C+A)。
我们对于这些二元变量就可以直接利用 2-sat 建图。注意要判断 i 是否能取 hi,j 是否能取 hj。
2-sat 判断是否合法,合法时求任意方案都是基本功了,此处不再提。
那 d 个变量怎么办呢?注意到 d 很小,我们可以直接指数级暴力枚举。
如果枚举这 d 个变量取 A, B, C 中的哪一个,复杂度为 O(3^d*m),不能通过所有数据。
这个枚举太不优雅了,我们尝试枚举过后把这 d 个变量加入 2-sat 系统中。
枚举这 d 个变量是强制为 A,或者是 B 与 C 中的某一个。后一种情况直接在 2-sat 中建边即可。
那么就可以 O(2^d*m) AC 这道题了。
@accepted code@
#include <vector>
#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 2*50000;
const int MAXM = 2*150000;
struct Graph{
struct edge{
int to; edge *nxt;
}edges[MAXM + 5], *adj[MAXN + 5], *ecnt;
void addedge(int u, int v) {
edge *p = (++ecnt);
p->to = v, p->nxt = adj[u], adj[u] = p;
}
void clear(int n) {
ecnt = edges;
for(int i=0;i<n;i++)
adj[i] = NULL;
}
}G;
int dfn[MAXN + 5], low[MAXN + 5], dcnt;
int stk[MAXN + 5], tp, id[MAXN + 5], tot;
void dfs(int x) {
dfn[x] = low[x] = (++dcnt), stk[++tp] = x;
for(Graph::edge *p=G.adj[x];p;p=p->nxt) {
if( !dfn[p->to] )
dfs(p->to), low[x] = min(low[x], low[p->to]);
else if( !id[p->to] )
low[x] = min(low[x], dfn[p->to]);
}
if( low[x] >= dfn[x] ) {
tot++;
do {
id[stk[tp]] = tot;
}while( stk[tp--] != x );
}
}
int n, m, d;
bool solve() {
for(int i=0;i<(n<<1);i++)
dfn[i] = low[i] = id[i] = 0;
dcnt = tot = tp = 0;
for(int i=0;i<(n<<1);i++)
if( !dfn[i] ) dfs(i);
for(int i=0;i<n;i++)
if( id[i<<1] == id[i<<1|1] )
return false;
return true;
}
int tag[MAXN + 5];
int c[3][MAXN + 5];
vector<int>vec[MAXN + 5];
void print() {
for(int i=0;i<(n<<1);i++)
vec[id[i]].push_back(i);
for(int i=1;i<=tot;i++) tag[i] = 1;
for(int i=1;i<=tot;i++) {
if( tag[i] == 1 ) {
for(int j=0;j<vec[i].size();j++) {
for(Graph::edge *p=G.adj[vec[i][j]];p;p=p->nxt)
if( tag[id[p->to]] == 0 ) {
tag[i] = 0;
break;
}
if( tag[i] == 0 ) break;
}
}
if( tag[i] == 1 ) {
for(int j=0;j<vec[i].size();j++)
tag[id[vec[i][j]^1]] = 0;
}
}
for(int i=0;i<n;i++)
for(int j=0;j<3;j++)
if( c[j][i] != -1 && tag[id[i<<1|c[j][i]]] )
putchar(j + 'A');
}
int xp[10], cnt;
int u[MAXM + 5], v[MAXM + 5], cu[MAXM + 5], cv[MAXM + 5];
char S[MAXN + 5], op[5];
int main() {
scanf("%d%d%s", &n, &d, S);
for(int i=0;i<n;i++)
if( S[i] == 'x' ) xp[cnt++] = i;
else {
int ch = S[i] - 'a';
c[ch][i] = -1, c[(ch+1)%3][i] = 0, c[(ch+2)%3][i] = 1;
}
scanf("%d", &m);
for(int i=0;i<m;i++) {
scanf("%d%s", &u[i], op), cu[i] = op[0] - 'A', u[i]--;
scanf("%d%s", &v[i], op), cv[i] = op[0] - 'A', v[i]--;
}
int t = (1<<d);
for(int s=0;s<t;s++) {
G.clear(n << 1);
for(int i=0;i<d;i++) {
if( !((s >> i) & 1) )
c[0][xp[i]] = -1, c[1][xp[i]] = 0, c[2][xp[i]] = 1;
else {
c[0][xp[i]] = 0, c[1][xp[i]] = c[2][xp[i]] = -1;
G.addedge(xp[i]<<1|1, xp[i]<<1);
}
}
for(int i=0;i<m;i++) {
if( c[cu[i]][u[i]] != -1 ) {
int p = c[cu[i]][u[i]];
if( c[cv[i]][v[i]] == -1 )
G.addedge(u[i]<<1|p, u[i]<<1|(!p));
else {
int q = c[cv[i]][v[i]];
G.addedge(u[i]<<1|p, v[i]<<1|q);
G.addedge(v[i]<<1|(!q), u[i]<<1|(!p));
}
}
}
if( solve() ) {
print();
return 0;
}
}
puts("-1");
}
@detail@
复习 2-sat 中.jpg