bzoj4549 LOJ2305 NOI2017 游戏 2-sat

题目链接:(因为bzoj的spj有问题,建议去loj或者洛谷提交)loj链接

首先考虑没有x的情况,因为每张地图都只不适合一辆车参加,反过来说就是只适合两辆车参加

那么我们完全可以不考虑这两辆车是AB,AC还是BC,都抽象成一辆和另一辆。

对于m组限制条件,如果第i场用u车,那么第j场则必须用j车。

分情况讨论,u表示选第一辆车,u′表示选另一辆车。

1、如果第i张地图本来就不适合u车,说明选u车一定无解,不连任何边。

2、如果第i张地图适合u车,但第j张地图不适合v车,说明第i张地图选u导致第j张地图选v无解,所以第i张地图只能选另一辆车,连边u->u′。

3、如果两张地图都适合,那么连边u->v,由逆否命题对称,再连边v′->u′,表示第j张地图如果选用v′那么第i张地图不能用u,即用u′。

连完边后跑2-sat就好了。

现在考虑有x的情况。

因为x能有三种选择,问题就不是2-sat了。瞅了一眼数据范围:x最多只有8个。那么我们可以2^8暴力枚举x不适合哪辆车,每遍做一次2-sat就行了。

无解情况就是2^8遍跑完后还没有解。

代码:

#include<bits/stdc++.h>
#define maxn 100005
using namespace std;
typedef long long LL;
int read()
{
	char c;int sum=0,f=1;c=getchar();
	while(c<'0' || c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0' && c<='9'){sum=sum*10+c-'0';c=getchar();}
	return sum*f;
}
char get()
{
	char c=getchar();
	while(c!='A' && c!='B' && c!='C') c=getchar();
	return c;
}
int n,d,m,dep;
char s[maxn],ss[maxn];
int pos[maxn];
int dfn[maxn],low[maxn],bel[maxn];
bool vis[maxn],flag;
stack<int> S;
vector<int> edge[maxn];
int a1[maxn],b1[maxn];
char a2[maxn],b2[maxn];
int num,cur;
void dfs(int x)
{
	S.push(x);
	dfn[x]=low[x]=++cur;vis[x]=1;
	int lens=edge[x].size();
	for(int i=0;i<lens;i++)
	{
		int nex=edge[x][i];
		if(!dfn[nex])
		{
			dfs(nex);
			low[x]=min(low[x],low[nex]);
		}
		else if(vis[nex])
		low[x]=min(low[x],dfn[nex]);
	}
	if(dfn[x]==low[x])
	{
		int t;
		num++;
		do
		{
			t=S.top();S.pop();
			vis[t]=0;bel[t]=num; 
		}
		while(t!=x);
	}
}
void tarjan()
{
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(vis,0,sizeof(vis));
	memset(bel,0,sizeof(bel));
	num=cur=0;
	for(int i=1;i<=n<<1;i++)
	if(!dfn[i]) dfs(i);
}
int cal(int x,char c)
{
	if(s[x]=='a') return c=='B'?x:x+n;
	if(s[x]=='b' || s[x]=='c') return c=='A'?x:x+n;
	if(c=='C') return x+n;
	return x;
}
int rev(int x)
{
	return x>n?x-n:x+n;
}
void add(int u,int v)
{
	edge[u].push_back(v);
}
bool solve()
{
	for(int i=1;i<=n<<1;i++)
	edge[i].clear();
	for(int i=1;i<=m;i++)
	{
		if(s[a1[i]]!='x' && s[b1[i]]!='x')
		{
			if(s[a1[i]]-32==a2[i]) continue;
			int u=cal(a1[i],a2[i]),v;
			if(s[b1[i]]-32==b2[i])
			{
				add(u,rev(u));
				continue;
			}
			v=cal(b1[i],b2[i]);
			add(u,v);add(rev(v),rev(u));
		}
		else
		{
			char x=s[a1[i]],y=s[b1[i]];
			int u,v,p1=pos[a1[i]],p2=pos[b1[i]];
			if(x=='x' && y=='x')
			{
				if(a2[i]==ss[p1]) continue;
				u=cal(a1[i],a2[i]);
				if(b2[i]==ss[p2])
				{
					add(u,rev(u));
					continue;
				}
				v=cal(b1[i],b2[i]);
				add(u,v);add(rev(v),rev(u));
			}
			else if(x!='x' && y=='x')
			{
				if(s[a1[i]]-32==a2[i]) continue;
				u=cal(a1[i],a2[i]);
				if(b2[i]==ss[p2])
				{
					add(u,rev(u));
					continue;
				}
				v=cal(b1[i],b2[i]);
				add(u,v);add(rev(v),rev(u));
			}
			else
			{
				if(a2[i]==ss[p1]) continue;
				u=cal(a1[i],a2[i]);
				if(b2[i]==s[b1[i]]-32)
				{
					add(u,rev(u));
					continue;
				}
				v=cal(b1[i],b2[i]);
				add(u,v);add(rev(v),rev(u));
			}
		}
	}
	tarjan();
	for(int i=1;i<=n;i++)
	if(bel[i]==bel[i+n]) return false;
	for(int i=1;i<=n;i++)
	{
        if(bel[i]<bel[i+n])
		{
            if(s[i]=='a') putchar('B');
            else if (s[i]=='b' || s[i]=='C') putchar('A');
            else if (ss[pos[i]]=='A') putchar('B');
            else putchar('A');
        }
        else
		{
            if(s[i]=='a' || s[i]=='b') putchar('C');
            else if (s[i]=='c') putchar('B');
            else if (ss[pos[i]]== 'A') putchar('C');
            else putchar('B');
        }
    }
    return true;
}
void dfs2(int dd)
{
	if(dd>dep)
	{
		if(!flag) flag=solve();
		if(flag) exit(0);
		return;
	}
	ss[dd]='A';dfs2(dd+1);
	ss[dd]='B';dfs2(dd+1);
}
int main()
{
	n=read();d=read();
	scanf("%s",s+1);
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='x')
		pos[i]=++dep;
	}
	m=read();
	for(int i=1;i<=m;i++)
	a1[i]=read(),a2[i]=get(),b1[i]=read(),b2[i]=get();
	dfs2(1);
	if(!flag)
	puts("-1");
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值