zoukankan      html  css  js  c++  java
  • 【BZOJ4945】[Noi2017]游戏 2-SAT

    【BZOJ4945】[Noi2017]游戏

    题目描述

    题解:2-SAT学艺不精啊!

    这题一打眼看上去是个3-SAT?哎?3-SAT不是NPC吗?哎?这题x怎么只有8个?暴力走起!

    因为x要么不是A要么不是B,所以直接2^8枚举所有x就行了。然后就变成了一个2-SAT问题。假设有两场游戏1,2,分别可以使用的地图为A1,A2,B1,B2,如果有一个限制是1 A 2 A,那么选A1就必须选A2,然后我这个沙茶就开开心心的拿了55分。

    为什么不对?我建出来的图显然不对偶啊!考虑逆否命题,选A1就必须选A2,那么选B2就必须选B1啊!然后跑2-SAT+拓扑排序输出方案即可。

    特别地,如果选A1就必须选A2,但是A1不能选,那么我们可以直接无视这个条件。如果选A1就必须选A2,但是A2不能选,那么A1也不能选, 于是就连一条A1->B1的边(你可以理解为这样以来,在反向图中B1的拓扑序在A1前面,所以会先选B1。但真正用意好像不是这个~)。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <queue>
    #define P(A,B) ((B-1)*n+A)
    using namespace std;
    const int maxn=200010;
    int n,D,m,sum,tot,top,cnt,flag;
    int to[maxn],next[maxn],head[maxn],tt[maxn],nn[maxn],hh[maxn],op[maxn],ot[maxn];
    int del[maxn],ins[maxn],dep[maxn],low[maxn],sta[maxn],bel[maxn],pos[10];
    int pa[maxn],pb[maxn],pc[maxn],pd[maxn],color[maxn],d[maxn];
    char str[maxn],c1[5],c2[5],ans[maxn];
    queue<int> q;
    void add(int a,int b)
    {
    	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
    }
    void ADD(int a,int b)
    {
    	tt[cnt]=b,nn[cnt]=hh[a],hh[a]=cnt++;
    }
    void tarjan(int x)
    {
    	dep[x]=low[x]=++tot,ins[x]=1,sta[++top]=x;
    	for(int i=hh[x];i!=-1;i=nn[i])
    	{
    		int y=tt[i];
    		if(!dep[y])	tarjan(y),low[x]=min(low[x],low[y]);
    		else	if(ins[y])	low[x]=min(low[x],dep[y]);
    	}
    	if(dep[x]==low[x])
    	{
    		int t;
    		sum++;
    		do
    		{
    			t=sta[top--],ins[t]=0,bel[t]=sum;
    		}while(t!=x);
    	}
    }
    void check()
    {
    	memset(hh,-1,sizeof(hh));
    	memset(dep,0,sizeof(dep));
    	cnt=tot=sum=0;
    	int i,a,b,c;
    	for(i=1;i<=n;i++)
    	{
    		a=(str[i]-'a')*n+i,b=(a-1+n)%(3*n)+1,c=(b-1+n)%(3*n)+1;
    		del[a]=1,del[b]=del[c]=0;
    		op[b]=c,op[c]=b;
    	}
    	for(i=1;i<=m;i++)
    	{
    		a=pc[i]*n+pa[i],b=pd[i]*n+pb[i];
    		if(a==b||del[a])	continue;
    		if(pa[i]==pb[i]||del[b])
    		{
    			ADD(a,op[a]);
    			continue;
    		}
    		ADD(op[b],op[a]),ADD(a,b);
    	}
    	for(i=1;i<=3*n;i++)	if(!del[i]&&!dep[i])	tarjan(i);
    	for(i=1;i<=3*n;i++)
    	{
    		if(del[i])	continue;
    		if(bel[op[i]]==bel[i])	return ;
    		else	ot[bel[op[i]]]=bel[i],ot[bel[i]]=bel[op[i]];
    	}
    	flag=1;
    }
    void dfs(int x)
    {
    	if(x==D+1)
    	{
    		check();
    		return ;
    	}
    	str[pos[x]]='a',dfs(x+1);
    	if(flag)	return ;
    	str[pos[x]]='b',dfs(x+1);
    }
    void DFS(int x)
    {
    	if(color[x]!=-1)	return ;
    	color[x]=0,color[ot[x]]=1;
    	for(int i=head[x];i!=-1;i=next[i])	DFS(to[i]);
    }
    int main()
    {
    	scanf("%d%d%s%d",&n,&D,str+1,&m);
    	int i,j,u;
    	for(i=1;i<=n;i++)	if(str[i]=='x')	pos[++pos[0]]=i;
    	for(i=1;i<=m;i++)
    	{
    		scanf("%d%s%d%s",&pa[i],c1,&pb[i],c2),pc[i]=c1[0]-'A',pd[i]=c2[0]-'A';
    	}
    	dfs(1);
    	if(!flag)
    	{
    		printf("-1");
    		return 0;
    	}
    	memset(head,-1,sizeof(head));
    	memset(color,-1,sizeof(color));
    	cnt=0;
    	for(i=1;i<=3*n;i++)	if(!del[i])
    	{
    		for(j=hh[i];j!=-1;j=nn[j])	if(bel[tt[j]]!=bel[i])	d[bel[i]]++,add(bel[tt[j]],bel[i]);
    	}
    	for(i=1;i<=sum;i++)	if(!d[i])	q.push(i);
    	while(!q.empty())
    	{
    		u=q.front(),q.pop();
    		for(i=head[u];i!=-1;i=next[i])
    		{
    			d[to[i]]--;
    			if(!d[to[i]])	q.push(to[i]);
    		}
    		if(color[u]!=-1)	continue;
    		DFS(ot[u]);
    	}
    	for(i=1;i<=3*n;i++)	if(!del[i]&&color[bel[i]]==1)	ans[(i-1)%n]=(i-1)/n+'A';
    	printf("%s",ans);
    	return 0;
    }
  • 相关阅读:
    ASP.NET使用UEditor入门与常见问题
    关于发布者策略程序集学习记录
    Myeclipse 10安装,以及Flex4插件(原)
    IE、Chrome等浏览器实现PDF预览(原)
    Oracle数据库中文显示乱码的最简单解决办法
    关于程序集的结构(2)C#和.NET2.0实战学习笔记
    关于AppDomain
    关于强名称程序集 C#和.NET2.0实战学习记录
    数据库查询·聚合分支格式化日期·思维导图&要点&误点(含示例)
    如何在SERVER2003上安装MySQL?(附安装教程及资源地址)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7236689.html
Copyright © 2011-2022 走看看