[NOI2017]游戏「2-SAT」

[NOI2017]游戏「2-SAT」

题目描述

小 L 计划进行 \(n\) 场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。

小 L 的赛车有三辆,分别用大写字母 \(A\)\(B\)\(C\) 表示。地图一共有四种,分别用小写字母 \(x\)\(a\)\(b\)\(c\) 表示。

其中,赛车 \(A\) 不适合在地图 \(a\) 上使用,赛车 \(B\) 不适合在地图 \(b\) 上使用,赛车 \(C\) 不适合在地图 \(c\) 上使用,而地图 \(x\) 则适合所有赛车参加。

适合所有赛车参加的地图并不多见,最多只会有 \(d\) 张。

\(n\) 场游戏的地图可以用一个小写字母组成的字符串描述。例如:\(S=\texttt{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)\) 来描述,表示若在第 $i4 场使用型号为 \(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\) 场游戏中如何安排赛车的使用。如果存在多组解,输出其中任意一组即可。

输入输出样例

输入 #1

3 1
xcc
1
1 A 2 B

输出 #1

ABA

思路分析

来,今天我们学习\(2-SAT\),首先从这个黑题入手

  • 每个地图都有两个或三个状态可选,另加了一些限制,显然是一道 \(2—SAT\) 的题
  • 关键在于对于 \(x\) 地图,有三种状态,难不成弄个 \(3—SAT\) ?然而这个做法已被证明是 \(NPC\)
  • 为了变成 \(2-SAT\) 问题,我们将 $x $地图分类讨论,第一种是不能选 \(A\) ,第二种不能选 \(B\),这时候选 \(A\)\(B\)\(C\) 的情况就都已经囊括了,不须再进一步枚举
  • 接下来是连边,对于每个四元组 \(i,h_i,j,h_j\)
    • \(h_i\) 是不可选的,不需连边,跳过即可
    • \(h_j\) 是不可选的,则需要建边强制i不能选 \(h_i\)
    • 都可选,直接连边,表示同时选,另外再根据逆否命题建一个反向边,即同时不选
  • 对于方案输出,输出拓扑序大的,即缩点编号小的即可
    细节真的超多

Code

#include
#include
#include
#include
#define N 500100
using namespace std;
inline int read(){
	int x = 0,f = 1;
	char ch = getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*f;
}
int n,d,m,dfs_clock,scc_cnt,dfn[N],low[N],head[N],sta[N],top,belong[N],in[N],a[N],b[N],gn[N];
char s[N],h1[N],h2[N],ans[N];
struct edge{
	int to,next;
}e[N<<2];
int len;
void addedge(int u,int v){
	e[++len].to = v;
	e[len].next = head[u];
	head[u] = len;
}
void Init(){ //每次改变x图的状态都要重新建图
	memset(head,0,sizeof(head));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(belong,0,sizeof(belong));
	memset(in,0,sizeof(in));
	memset(sta,0,sizeof(sta));
	dfs_clock = scc_cnt = top  = len = 0;
}
void Tarjan(int u){ //Tarjan缩点
	dfn[u] = low[u] = ++dfs_clock;
	sta[++top] = u;
	in[u] = 1;
	for(int i = head[u];i;i=e[i].next){
		int v = e[i].to;
		if(!dfn[v]){
			Tarjan(v);
			low[u] = min(low[u],low[v]);
		}
		else if(!belong[v])low[u] = min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		scc_cnt++;
		while(1){
			int x = sta[top--];
			belong[x] = scc_cnt;
			in[x] = 0;
			if(u==x)break;
		}
	}
}
bool check(){//寻找可行方案
	for(int i = 1;i <= (n<<1);i++)if(!dfn[i])Tarjan(i);
	for(int i = 1;i <= n;i++){
		if(belong[i]==belong[i+n])return false; //两个状态出现在了一个scc里,就没有合理方案
		if(belong[i]

你可能感兴趣的:([NOI2017]游戏「2-SAT」)