[BZOJ4945][NOI2017]游戏
sol
先考虑\(d=0\)怎么做吧。
每个位置上只有两种选法,相当于每个位置是一个布尔变量,选第一种方案布尔值为0,选第二种方案布尔值为1。
然后考虑一组限制条件\((i,h_i,j,h_j)\),设\(u\)表示\([\)在\(i\)位置上选\(h_i]\),\(v\)表示\([\)在\(j\)位置上选\(h_j]\)。
可以分三种情况讨论:
1、如果在\(i\)位置上填\(h_i\)不合法,那这个限制条件就是没用的。直接忽略。
2、如果在\(i\)位置上填\(h_i\)合法而在\(j\)位置上填\(h_j\)不合法,那么就不能在\(i\)位置上填\(h_i\)。连边\(u \to u'\)
3、如果在两个位置上填均合法,那么就连边\(u \to v\)。注意!2-SAT问题在连边时要同时对逆否命题连边!所以还要连一条\(v' \to u'\)。
然后跑\(2-SAT\),跑完以后:
1、判断合法性:检查\(i\)和\(i'\)是否在同一个强连通分量中。
2、输出方案:由于缩强连通分量后图变成了一个\(DAG\),同时一个条件可以推得其后继的所有条件,所以一定是优先选拓扑序靠后的。鉴于在\(Tarjan\)中强连通分量从后往前标号,所以只要选择标号较小的条件就好了。
这样子对于\(d=0\)的部分就可以直接\(O(n+m)\)解决了。
那么\(d>0\)?
发现\(d\le8\),感觉可以暴搜?复杂度\(O(3^d(n+m))\)?
发现我们枚举的其实是不能选哪个,所以其实只要对每个\(x\)位置枚举两个就可以了。
复杂度\(O(2^d(n+m))\)
code
在uoj上被hack了。。。
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int gi()
{
int x=0,w=1;char ch=getchar();
while ((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
if (ch=='-') w=0,ch=getchar();
while (ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return w?x:-x;
}
const int N = 2e5+5;
int n,d,pos[9],m,p1[N],c1[N],p2[N],c2[N];
char s[N];
int to[N],nxt[N],head[N],cnt,dfn[N],low[N],tim,Stack[N],top,vis[N],bel[N],scc;
void link(int u,int v){to[++cnt]=v;nxt[cnt]=head[u];head[u]=cnt;}
void Tarjan(int u)
{
dfn[u]=low[u]=++tim;
Stack[++top]=u;vis[u]=1;
for (int e=head[u];e;e=nxt[e])
if (!dfn[to[e]]) Tarjan(to[e]),low[u]=min(low[u],low[to[e]]);
else if (vis[to[e]]) low[u]=min(low[u],dfn[to[e]]);
if (dfn[u]==low[u])
{
++scc;int v;
do{
v=Stack[top--];
vis[v]=0;bel[v]=scc;
}while (u!=v);
}
}
void work()
{
memset(head,0,sizeof(head));cnt=0;
for (int i=1;i<=m;++i)
{
int u=(c1[i]-s[p1[i]]+3)%3;
int v=(c2[i]-s[p2[i]]+3)%3;
if (u)
if (v)
{
u>>=1;v>>=1;
link(p1[i]+u*n,p2[i]+v*n);
link(p2[i]+(v^1)*n,p1[i]+(u^1)*n);
}
else
{
u>>=1;
link(p1[i]+u*n,p1[i]+(u^1)*n);
}
}
memset(dfn,0,sizeof(dfn));tim=scc=0;
for (int i=1;i<=2*n;++i) if (!dfn[i]) Tarjan(i);
for (int i=1;i<=n;++i) if (bel[i]==bel[i+n]) return;
for (int i=1;i<=n;++i)
if (bel[i]<bel[i+n]) putchar((s[i]+1)%3+'A');
else putchar((s[i]+2)%3+'A');
puts("");exit(0);
}
void dfs(int i)
{
if (i==d+1) {work();return;}
s[pos[i]]=0;dfs(i+1);
s[pos[i]]=1;dfs(i+1);
}
int main()
{
n=gi();gi();scanf("%s",s+1);
for (int i=1;i<=n;++i)
if (s[i]=='x') pos[++d]=i;
else s[i]-='a';
m=gi();
for (int i=1;i<=m;++i)
p1[i]=gi(),c1[i]=getchar()-'A',p2[i]=gi(),c2[i]=getchar()-'A';
dfs(1);
return puts("-1"),0;
}