X.[NOI2017] 游戏
因为并没有专门开2SAT笔记,所以就放这了
好久没用2SAT了,都忘光了……
首先,我们可以 (2^d) 枚举所有 x
型赛道是当 a
型用还是当 b
型用,因为 a
型允许你选 B
和 C
,而 b
型又允许你选 A
,这样就涵盖了全部情形。
这样之后,我们便考虑建立2SAT模型。
对于一组限制 ((u,v)),若 (u) 选择的颜色是被禁止的颜色,显然可以不予考虑;若 (v) 选择的颜色是被禁止的颜色,显然这会使得选 (u) 不合法;怎么让选 (u) 不合法呢?连边 ((u,u')) 即可。
否则,其就是一条常规的限制,连边 ((u,v)),同时也别忘记连边 ((v',u')),因为若 (v) 没有选择强制它选择的颜色,则 (u) 也一定不能选择具有强制效力的颜色。
于是直接Tarjan跑2SAT即可。时间复杂度 (OBig((n+m)2^dBig))。
代码:
#include<bits/stdc++.h>
using namespace std;
int a[3][3]={{-1,0,1},{0,-1,1},{0,1,-1}},b[3][2]={{1,2},{0,2},{0,1}};
int n,d,m,tp[50010];
char str[50010];
struct EDGE{
int u,v,cu,cv;
EDGE(){}
EDGE(int U,int V,int CU,int CV){u=U,v=V,cu=CU,cv=CV;}
}e[100100];
vector<int>v[100010],u;
int dfn[100010],low[100010],tot,col[100010],c;
stack<int>s;
void Tarjan(int x){
dfn[x]=low[x]=++tot,s.push(x);
for(auto y:v[x]){
if(!dfn[y])Tarjan(y),low[x]=min(low[x],low[y]);
else if(!col[y])low[x]=min(low[x],dfn[y]);
}
if(dfn[x]>low[x])return;
c++;
while(s.top()!=x)col[s.top()]=c,s.pop();
col[s.top()]=c,s.pop();
}
bool che(){
tot=c=0;for(int i=1;i<=2*n;i++)dfn[i]=low[i]=col[i]=0,v[i].clear();
for(int i=1;i<=m;i++){
int X=a[tp[e[i].u]][e[i].cu],Y=a[tp[e[i].v]][e[i].cv];
// printf("%d %d
",X,Y);
if(e[i].cu==tp[e[i].u])continue;
if(e[i].cv==tp[e[i].v])v[X*n+e[i].u].push_back((!X)*n+e[i].u);else v[X*n+e[i].u].push_back(Y*n+e[i].v),v[(!Y)*n+e[i].v].push_back((!X)*n+e[i].u);
}
for(int i=1;i<=2*n;i++)if(!dfn[i])Tarjan(i);
for(int i=1;i<=n;i++)if(col[i]==col[i+n])return false;
for(int i=1;i<=n;i++)putchar('A'+b[tp[i]][(col[i]>col[i+n])]);putchar('
');
return true;
}
int main(){
scanf("%d%d",&n,&d);
scanf("%s",str+1);
for(int i=1;i<=n;i++)if(str[i]=='x')u.push_back(i);else tp[i]=str[i]-'a';
scanf("%d",&m);
for(int i=1,x,y;i<=m;i++)scanf("%d%s%d%s",&x,str,&y,str+10),e[i]=EDGE(x,y,str[0]-'A',str[10]-'A');
for(int i=0;i<(1<<u.size());i++){
for(int j=0;j<u.size();j++)tp[u[j]]=(i>>j)&1;
if(che())return 0;
}
puts("-1");
return 0;
}