1032. 游戏
题目链接
1032. 游戏
狂野飙车是小 \(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,h_i,j,h_j\)) 来描述,表示若在第 \(i\) 场使用型号为 \(h_i\) 的车子,则第 \(j\) 场游戏要使用型号为 \(h_j\) 的车子。
你能帮小 \(L\) 选择每场游戏使用的赛车吗?
如果有多种方案,输出任意一种方案。
如果无解,输出 “\(-1\)”(不含双引号)。
输入格式
输入第一行包含两个非负整数 \(n,d\)。
输入第二行为一个字符串 \(S\) 。
\(n,d,S\) 的含义见题目描述,其中 \(S\) 包含 \(n\) 个字符,且其中恰好 \(d\) 个为小写字母 \(x\)。
输入第三行为一个正整数 \(m\) ,表示有 \(m\) 条用车规则。
接下来 \(m\) 行,每行包含一个四元组 \(i,h_i,j,h_j\) ,其中 \(i, j\) 为整数,\(h_i,h_j\) 为字符 \(A 、B\) 或 \(C\),含义见题目描述。
输出格式
输出一行。
若无解输出 “\(-1\)”(不含双引号)。
若有解,则包含一个长度为 \(n\) 的仅包含大写字母 \(A、B、C\) 的字符串,表示小 \(L\) 在这 \(n\) 场游戏中如何安排赛车的使用。
如果存在多组解,输出其中任意一组即可。
数据范围
输入样例:
3 1
xcc
1
1 A 2 B
输出样例:
ABA
样例解释
小 \(L\) 计划进行 \(3\) 场游戏,其中第 \(1\) 场的地图类型是 \(x\),适合所有赛车,第 \(2\) 场和第 \(3\) 场的地图是 \(c\),不适合赛车 \(C\)。
小 \(L\) 希望:若第 \(1\) 场游戏使用赛车 \(A\),则第 \(2\) 场游戏使用赛车 \(B\)。
那么为这 \(3\) 场游戏分别安排赛车 \(A、B、A\) 可以满足所有条件。
若依次为 \(3\) 场游戏安排赛车为 \(BBB\) 或 \(BAA\) 时,也可以满足所有条件,也被视为正确答案。
但依次安排赛车为 \(AAB\) 或 \(ABC\) 时,因为不能满足所有条件,所以不被视为正确答案。
解题思路
2-SAT
对于 \(a\),其只能使用 \(A\) 或 \(B\),对于 \(b\),其只能使用 \(a\) 或 \(c\),对于 \(c\),其只能使用 \(A\) 或 \(B\),对于 \(x\),其可以使用 \(A\) 或 \(B\) 或 \(C\),显然这是一个 2-SAT 和 3-SAT 结合的问题,即如果不考虑 \(x\) 存在的情况(即只存在 2-SAT 的情况),对于若干个条件,即如果第 \(i\) 场使用 \(x\),则第 \(j\) 场应该使用 \(y\),如果第 \(i\) 场本身就不能使用 \(x\),则显然该条件没用,可以忽略,否则如果第 \(j\) 场本身不能使用 \(y\),则应该使条件不成立,即第 \(i\) 场不应该使用 \(x\),否则如果第 \(j\) 场能使用 \(y\) 的话,则应该使第 \(i\) 场使用 \(x\) 这个命题表示的节点指向第 \(j\) 场使用 \(y\) 这个命题表示的节点,同时逆否命题也需要加入边,但是回到问题本身, 3-SAT 是一个 NPC 问题,但是由于 \(x\) 的个数不是很多,但如果暴力枚举所有 \(x\) 的值的话也不行,但可以将 3-SAT 问题转化为 2-SAT 问题,即对于有 \(x\) 的场次来说,其一定是选 \(B\) 或 \(C\),或者选 \(A\) 或 \(C\),即其可以等价于两种 2-SAT,共有 \(2^d\) 种组合,这样就将问题完全转化为 2-SAT 问题
- 时间复杂度:\(O(2^d\times (n+m)\)
代码
// Problem: 游戏
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/1034/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=100005,M=2e5+5;
int n,d,m;
char s[N];
int h[N],ne[M],e[M],idx;
int pos[10];
int dfn[N],low[N],timestamp,stk[N],top,scc_cnt,id[N];
bool in_stk[N];
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++;
}
void tarjan(int x)
{
dfn[x]=low[x]=++timestamp;
stk[++top]=x,in_stk[x]=true;
for(int i=h[x];~i;i=ne[i])
{
int y=e[i];
if(!dfn[y])
{
tarjan(y);
low[x]=min(low[x],low[y]);
}
else if(in_stk[y])
low[x]=min(low[x],dfn[y]);
}
if(low[x]==dfn[x])
{
int y;
scc_cnt++;
do
{
y=stk[top--];
in_stk[y]=false;
id[y]=scc_cnt;
}while(y!=x);
}
}
int get(int x,char a,int t)
{
a-='A';
char b=s[x]-'a';
if(((b+1)%3==a)^t)return x<<1|1;
return x<<1;
}
char put(int x,int t)
{
int y=s[x]-'a';
return 'A'+(y+t)%3;
}
bool work()
{
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(a+32!=s[x])
{
if(b+32!=s[y])add(get(x,a,1),get(y,b,1)),add(get(y,b,0),get(x,a,0));
else
add(get(x,a,1),get(x,a,0));
}
}
for(int i=0;i<2*n;i++)
if(!dfn[i])tarjan(i);
for(int i=0;i<n;i++)
if(id[i<<1]==id[i<<1|1])return false;
for(int i=0;i<n;i++)
if(id[i<<1]<id[i<<1|1])putchar(put(i,1));
else
putchar(put(i,2));
return true;
}
int main()
{
scanf("%d%d",&n,&d);
scanf("%s",s);
for(int i=0,j=0;s[i];i++)
if(s[i]=='x')pos[j++]=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);
for(int i=0;i<1<<d;i++)
{
for(int j=0;j<d;j++)
if(i>>j&1)s[pos[j]]='a';
else
s[pos[j]]='b';
if(work())return 0;
}
puts("-1");
return 0;
}