[NOI2017]游戏(2-SAT+枚举)
题目
解法
2-SAT板子题
如果没有x这个字符的话,问题变成每个点都有两种选法,有一些限制,显然是个2-SAT问题,设拆出来的两个点分别为\(i\)和\(i'\),则对于限制\(x->y\),连边\((x,y),(y',x')\),跑tarjan即可(2-SAT基操嘛。)
即使现在有了x这个字符,我们也可以假装一个点仍只有两种选法,枚举这两种选法,并且确保\(A,B,C\)至少在一种选法中存在就行了;具体来说,对于每个x,枚举\(A\)和\(B\)中哪一个不能选就足以表示出所有情况了
一句话就是:枚举将每个x变成a或者b,对每种情况跑最简单的\(2-SAT\)就行了
输出方案的话比较\(color[i]\)和\(color[i+n]\)即可,具体见输出部分
时间复杂度\(O(n*2^n)\)
Code
//因为spacial judge,最后一行不要输出回车
#include<bits/stdc++.h>
#define N 100005
#define Max(x,y) ((x)>(y)?(x):(y))
#define Min(x,y) ((x)<(y)?(x):(y))
using namespace std;
int n,m,d,ref[N][3],rev[N][3];
int dfn[N],low[N],c,col,color[N];
int st[N],top;
int ban[10];
char lim[N];
struct R { int x,dx,y,dy; } r[N];
struct Edge
{
int next,to;
}edge[N<<3];int head[N],cnt;
void add_edge(int from,int to)
{
edge[++cnt].next=head[from];
edge[cnt].to=to;
head[from]=cnt;
}
//0:A 1:B 2:C
template <class T>
void read(T &x)
{
char c;int sign=1;
while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
while((c=getchar())>='0'&&c<='9') x=x*10+c-48; x*=sign;
}
void in()
{
read(n);read(d);
scanf("%s",lim);
read(m);
for(int i=1;i<=m;++i)
{
char op[2];
read(r[i].x); scanf("%s",op); r[i].dx=op[0]-'A';
read(r[i].y); scanf("%s",op); r[i].dy=op[0]-'A';
}
}
void tarjan(int rt)
{
dfn[rt]=low[rt]=++c;
st[++top]=rt;
for(int i=head[rt];i;i=edge[i].next)
{
int v=edge[i].to;
if(!dfn[v])
{
tarjan(v);
low[rt]=Min(low[rt],low[v]);
}
else if(!color[v]) low[rt]=Min(low[rt],dfn[v]);
}
if(dfn[rt]==low[rt])
{
color[rt]=++col;
while(st[top]!=rt) color[st[top--]]=col;
--top;
}
}
bool work()
{
memset(head,0,sizeof(head));
memset(dfn,0,sizeof(dfn));
memset(ref,0,sizeof(ref));
memset(rev,0,sizeof(rev));
memset(color,0,sizeof(color));
cnt=top=c=col=0;
int ct=0;
for(int i=1;i<=n;++i)
{
int no,one,two;
if(lim[i-1]=='a') no=0,one=1,two=2;
else if(lim[i-1]=='b') no=1,one=0,two=2;
else if(lim[i-1]=='c') no=2,one=0,two=1;
else
{
++ct;
if(ban[ct]) no=0,one=1,two=2;
else no=1,one=0,two=2;
}
ref[i][no]=0,ref[i][one]=1,ref[i][two]=2;
rev[i][1]=one,rev[i][2]=two;
}
for(int i=1;i<=m;++i)
{
int x=r[i].x,dx=r[i].dx,y=r[i].y,dy=r[i].dy;
if(!ref[x][dx]) continue;//不能用这个车
else if(!ref[y][dy]) add_edge(x+(ref[x][dx]-1)*n , x+(ref[x][dx]-1 ? 0:n));//一用就死
else
{
add_edge(x+(ref[x][dx]-1)*n , y+(ref[y][dy]-1)*n);
add_edge(y+(ref[y][dy]-1?0:n) , x+(ref[x][dx]-1?0:n));//感觉这样子表示1,2节点好蠢。。。
}
}
for(int i=1,t=(n<<1);i<=t;++i) if(!dfn[i]) tarjan(i);
bool ret=1;
for(int i=1;i<=n;++i) if(color[i]==color[i+n]) ret=0;
return ret;
}
int main()
{
in();
bool ok=0;
for(int i=0,t=(1<<d);i<t;++i)
{
for(int j=0;j<d;++j) ban[j+1]=(i>>j&1);
if(work())
{
for(int i=1;i<=n;++i) printf("%c",color[i]<color[i+n] ? rev[i][1]+'A' : rev[i][2]+'A');
ok=1;
break;
}
}
if(!ok) printf("-1");
return 0;
}