[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; 
}

posted @ 2019-03-04 13:55  cloud_9  阅读(172)  评论(0编辑  收藏  举报