[BZOJ4945][NOI2017]游戏

bzoj
luogu

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;
}
posted @ 2018-03-28 16:23  租酥雨  阅读(210)  评论(3编辑  收藏  举报