[NOI2017]游戏
Description:
小 L 计划进行\(n\)场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。小 L 的赛车有三辆,分别用大写字母ABC表示。地图一共有四种,分别用小写字母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’’(不含双引号)。
Hint :
\(n\le 10^5\)
Solution :
考虑如果没有\(x\)地图,这就是个普通的\(2-SAT\)
但连边还是有细节的:
若 \(i\) 不适合 \(h_i\) 则直接忽视
若 \(i\) 适合 \(h_i\) 但 \(j\) 不适合 \(h_j\) ,则连边 \(i->i^{'}\) ,表示一定不能选 \(h_i\)
其他的就是一般情况了
现在加入\(x\)地图后变成了\(3-SAT\),为不可做的\(NP\)问题
观察到 \(x地图数量 \le 8\)
考虑枚举那个地图是 \(a\) 还是 \(b\)
是 \(a\) 则能走 \(b\) 和 \(c\)
是 \(b\) 则能走 \(a\) 和 \(c\)
这样便覆盖了所有情况
复杂度 \(O(2^8*(n+m))\) 足已通过本题
而这题还要求输出方案,那我们就按字典序连边和输出,注意要分类讨论
#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1
#define rs p<<1|1
using namespace std;
typedef long long ll;
const int mxn=1e5+5;
int n,m,d,k,cnt,tot,col;
int x[mxn],y[mxn],hd[mxn],bl[mxn],pos[mxn],dfn[mxn],low[mxn],ins[mxn];
char p[mxn],q[mxn],s[mxn],ans[mxn];
stack<int > st;
struct ed {
int to,nxt;
}t[mxn<<1];
inline void add(int u,int v) {
t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}
inline int read() {
char c=getchar(); int x=0,f=1;
while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
return x*f;
}
inline void chkmax(int &x,int y) {if(x<y) x=y;}
inline void chkmin(int &x,int y) {if(x>y) x=y;}
void clr() {
memset(dfn,0,sizeof(dfn));
memset(low,0,sizeof(low));
memset(ins,0,sizeof(ins));
memset(bl,0,sizeof(bl));
memset(hd,0,sizeof(hd));
col=cnt=tot=0;
}
void tj(int u)
{
dfn[u]=low[u]=++tot; ins[u]=1; st.push(u);
for(int i=hd[u];i;i=t[i].nxt) {
int v=t[i].to;
if(!dfn[v]) tj(v),chkmin(low[u],low[v]);
else if(ins[v]) chkmin(low[u],dfn[v]);
}
if(low[u]==dfn[u]) {
++col;
do {
bl[u]=col; u=st.top();
st.pop(); ins[u]=0;
} while(low[u]!=dfn[u]);
}
}
int check() {
for(int i=1;i<=2*n;++i)
if(!dfn[i]) tj(i);
for(int i=1;i<=n;++i) {
if(bl[i]==bl[i+n]) return 0;
if(bl[i]<bl[i+n]) ans[i]=(s[i]=='A'?'B':'A');
else ans[i]=(s[i]=='C'?'B':'C');
}
for(int i=1;i<=n;++i) printf("%c",ans[i]);
return 1;
}
void solve()
{
for(int i=0;i<(1<<d);++i) { //枚举x地图
clr();
for(int j=1;j<=d;++j) s[pos[j]]=((i>>(j-1))&1)?'A':'B';
for(int j=1;j<=m;++j) {
if(p[j]==s[x[j]]) continue ;
if(q[j]==s[y[j]]) {
if(p[j]=='C'||(p[j]=='B'&&s[x[j]]=='C')) add(x[j]+n,x[j]);
else add(x[j],x[j]+n);
continue ;
} //特殊情况
int tag1=0,tag2=0;
if(p[j]=='C'||(p[j]=='B'&&s[x[j]]=='C')) tag1=n;
if(q[j]=='C'||(q[j]=='B'&&s[y[j]]=='C')) tag2=n;
add(x[j]+tag1,y[j]+tag2); add(y[j]-tag2+n,x[j]-tag1+n);
//巧妙的分类讨论,避免了冗长的代码
}
if(check()) return ;
}
printf("-1");
}
int main()
{
n=read(); d=read();
scanf("%s",s+1); m=read();
for(int i=1;i<=n;++i) {
if(s[i]=='x') pos[++k]=i;
s[i]-=32;
}
for(int i=1;i<=m;++i) scanf("%d %c %d %c",&x[i],&p[i],&y[i],&q[i]);
solve();
return 0;
}