《P3825 [NOI2017] 游戏》
题目背景
【本题原题时限 1s】
狂野飙车是小 L 最喜欢的游戏。与其他业余玩家不同的是,小 L 在玩游戏之余,还精于研究游戏的设计,因此他有着与众不同的游戏策略。
题目描述
小 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
。
若有解,则包含一个长度为 n 的仅包含大写字母 A、B、C 的字符串,表示小 L 在这 n 场游戏中如何安排赛车的使用。如果存在多组解,输出其中任意一组即可。
输入输出样例
输入 #1复制
3 1 xcc 1 1 A 2 B
输出 #1复制
ABA
说明/提示
样例 1 解释
小 L 计划进行 3 场游戏,其中第 1 场的地图类型是 x,适合所有赛车,第 2 场和第 3 场的地图是 c,不适合赛车 C。
小 L 希望:若第 1 场游戏使用赛车 A,则第 2 场游戏使用赛车 B。
那么为这 3 场游戏分别安排赛车 A、B、A 可以满足所有条件。
若依次为 3 场游戏安排赛车为 BBB 或 BAA 时,也可以满足所有条件,也被视为正确答案。
但依次安排赛车为 AAB 或 ABC 时,因为不能满足所有条件,所以不被视为正确答案。
样例 2
详见附加文件。
数据范围
测试点编号 | n | d | m | 其他性质 |
---|---|---|---|---|
1 | ≤2 | 0 | ≤4 | 无 |
2 | ≤2 | ≤n | ≤4 | 无 |
3 | ≤5 | 0 | ≤10 | 无 |
4 | ≤5 | ≤n | ≤10 | 无 |
5 | ≤10 | 0 | ≤20 | 无 |
6 | ≤10 | ≤8 | ≤20 | 无 |
7 | ≤20 | 0 | ≤40 | S 中只包含 c |
8 | ≤20 | 0 | ≤40 | 无 |
9 | ≤20 | ≤8 | ≤40 | S 中只包含 x 或 c |
10 | ≤20 | ≤8 | ≤40 | 无 |
11 | ≤100 | 0 | ≤200 | S 中只包含 c |
12 | ≤100 | 0 | ≤200 | 无 |
13 | ≤100 | ≤8 | ≤200 | S 中只包含 x 或 c |
14 | ≤100 | ≤8 | ≤200 | 无 |
15 | ≤5×103 | 0 | ≤104 | 无 |
16 | ≤5×103 | ≤8 | ≤104 | S 中只包含 x 或 c |
17 | ≤5×103 | ≤8 | ≤104 | 无 |
18 | ≤5×104 | 0 | ≤105 | 无 |
19 | ≤5×104 | ≤8 | ≤105 | S 中只包含 x 或 c |
20 | ≤5×104 | ≤8 | ≤105 | 无 |
附件下载
game.zip1.38KB
代码实现:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 100010, M = 200010;
int n, d, m;
char s[N];
int h[N], e[M], ne[M], idx; // 邻接表
int dfn[N], low[N], timestamp; // Tarjan算法变量
int stk[N], top; // 栈
bool in_stack[N]; // 标记是否在栈中
int id[N], scc_cnt; // 记录节点所在强连通分量及数量
int pos[10]; // 记录'x'的位置
struct Op {
int x, y;
char a, b;
} op[M]; // 存储条件约束
// 添加有向边
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
// 返回变量状态编号:x选b(0)或!b(1)
int get_node(int x, char b, bool is_negate) {
char cur = s[x] - 'a';
b -= 'A';
if (((cur + 1) % 3 != b) ^ is_negate)
return x + n;
return x;
}
// 根据状态获取字符
char get_char(int x, bool is_negate) {
int cur = s[x] - 'a';
return 'A' + (cur + 3 + (is_negate ? -1 : 1)) % 3;
}
// Tarjan算法求强连通分量
void tarjan(int u) {
dfn[u] = low[u] = ++timestamp;
stk[++top] = u;
in_stack[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int v = e[i];
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (in_stack[v]) {
low[u] = min(low[u], dfn[v]);
}
}
if (dfn[u] == low[u]) {
scc_cnt++;
int v;
do {
v = stk[top--];
in_stack[v] = false;
id[v] = scc_cnt;
} while (v != u);
}
}
// 检查并输出解
bool solve() {
memset(h, -1, sizeof h);
memset(dfn, 0, sizeof dfn);
idx = timestamp = scc_cnt = 0;
for (int i = 0; i < m; i++) {
int x = op[i].x - 1, y = op[i].y - 1;
char a = op[i].a, b = op[i].b;
if (s[x] != a - 'A' + 'a') {
if (s[y] != b - 'A' + 'a') {
// 添加蕴含关系:a→b 和 ?b→?a
add(get_node(x, a, false), get_node(y, b, false));
add(get_node(y, b, true), get_node(x, a, true));
} else {
// a→?a(矛盾,a不能成立)
add(get_node(x, a, false), get_node(x, a, true));
}
}
}
// 对所有节点执行Tarjan
for (int i = 0; i < 2 * n; i++) {
if (!dfn[i]) tarjan(i);
}
// 检查是否有解
for (int i = 0; i < n; i++) {
if (id[i] == id[i + n]) return false;
}
// 输出解
for (int i = 0; i < n; i++) {
if (id[i] < id[i + n]) printf("%c", get_char(i, false));
else printf("%c", get_char(i, true));
}
return true;
}
int main() {
scanf("%d%d%s", &n, &d, s);
// 记录所有'x'的位置
int x_cnt = 0;
for (int i = 0; i < n; i++) {
if (s[i] == 'x') pos[x_cnt++] = i;
}
scanf("%d", &m);
for (int i = 0; i < m; i++) {
scanf("%d %c %d %c", &op[i].x, &op[i].a, &op[i].y, &op[i].b);
}
// 枚举所有'x'的可能取值
for (int mask = 0; mask < (1 << d); mask++) {
for (int i = 0; i < d; i++) {
s[pos[i]] = (mask >> i & 1) ? 'b' : 'a';
}
if (solve()) return 0;
}
// 无解
puts("-1");
return 0;
}