【UOJ】317-NOI2017-游戏-2-SAT

传送门:uoj317


题解

本蒟蒻也不知道为什么extra test 会WA 5啊,只能在bzoj4945上AC一下以安慰自己。
如果不了解2-SAT,大家可以看一下这份讲解,感觉还是很好懂,后面拓扑排序那里想一下就好了。
2-SAT算法很好理解,但很多人并不知道怎么输出方案,这里贴一篇博客,其中也解释到了排序缩点序和
拓扑序和方案选择之间的关系。
此题特殊在,它的边并不是对称的。
在处理操作时候可以这样:
hi h i 本身不成立,直接跳过。
hi h i 可行但 hj h j 不可行,就从 i i i连一条边,这样保证一旦选取了 i i ,就不可能成立。若两者都可行,就从i j j 连一条边,再从j i i ′ 连一条边(反否)。(后者满足对称性质,前者则不满足)。
以上是在不考虑x的情况下。
那么对于x,直接 28 2 8 枚举。为了不TLE,本蒟蒻当然要想方设法降低常数啊,具体看代码。


代码

#include<bits/stdc++.h>
using namespace std;

const int N=5e4+10; 
const int M=1e5+10;
int n,m;
int d,aa[M],bb[M],sta[N<<2],top;
int in[N],head[N<<1],to[M<<2],nxt[M<<2],tot,cir;
int dfn[N<<1],ins[N<<1],low[N<<1],cnt,bel[N<<1];
char s[N],a[M],b[M],ba[20];
bool flag=false,jud;

inline int rd()
{
   char ch=getchar();int x=0,f=1;
   while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
   while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}    
   return x*f;
}
inline char init()
{char ch=getchar();while(ch!='A' && ch!='B' && ch!='C') ch=getchar();return ch;}
inline int get(int x,char c)
{
    if(s[x]=='a') return c=='B'? x:x+n;
    else if(s[x]!='x') return c=='A'? x:x+n;
    else return c=='C'? x+n:x;
}
inline int neg(int x){return x>n? x-n:x+n;}
inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

inline void Tarjan(int x)
{
    dfn[x]=low[x]=++cnt;sta[++top]=x;ins[x]=1;
    for(int i=head[x];i;i=nxt[i]){
        if(!dfn[to[i]]){
            Tarjan(to[i]);
            low[x]=min(low[x],low[to[i]]);
        }else if(ins[to[i]]) low[x]=min(low[x],dfn[to[i]]);
    }
    if(dfn[x]==low[x])
    for(++cir;sta[top+1]!=x;top--) bel[sta[top]]=cir,ins[sta[top]]=0;
    if(bel[x]==bel[neg(x)] && bel[x]){
        jud=false;return;
    }
}

inline bool solve()
{
    int i;
    cnt=tot=cir=0;int u,v;
    for(i=1;i<=(n<<1);i++){head[i]=dfn[i]=bel[i]=ins[i]=0;}
    for(i=1;i<=m;i++){
        if(s[aa[i]]!='x' && s[bb[i]]!='x'){
            if(s[aa[i]]-32 == a[i]) continue;
            u=get(aa[i],a[i]);
            if(s[bb[i]]-32 == b[i]){lk(u,neg(u));continue;}
            v=get(bb[i],b[i]);lk(u,v);lk(neg(v),neg(u));
        }else{
            if(s[aa[i]]=='x'){
                if(ba[in[aa[i]]] == a[i]) continue;
                u=get(aa[i],a[i]);
                if(s[bb[i]]=='x'){
                    if(ba[in[bb[i]]] == b[i]){lk(u,neg(u));continue;}
                    v=get(bb[i],b[i]);lk(u,v);lk(neg(v),neg(u));
                }else{
                    if(s[bb[i]]-32 == b[i]){lk(u,neg(u));continue;}
                    v=get(bb[i],b[i]);lk(u,v);lk(neg(v),neg(u));
                }
            }else{
                if(s[aa[i]]-32 == a[i]) continue;
                u=get(aa[i],a[i]);
                if(ba[in[bb[i]]] == b[i]){lk(u,neg(u));continue;}
                v=get(bb[i],b[i]);lk(u,v);lk(neg(v),neg(u));
            }
        }
    }jud=true;
    for(i=1;i<=(n<<1) && jud;i++) if(!dfn[i]) Tarjan(i);
    if(!jud) return false;
    for(i=1;i<=n;i++){
        if(bel[i]<bel[i+n]){
            if(s[i]=='a') putchar('B');
            else if(s[i]!='x') putchar('A');
            else if(ba[in[i]]=='A') putchar('B');
            else putchar('A');
        }else{
            if(s[i]=='c') putchar('B');
            else putchar('C');
        }
    }
    return true;
}

inline void dfs(int depth)
{
    if(flag) return;
    if(depth==d+1){
        if(!flag) flag=solve();
        if(flag) exit(0);
        return; 
    }
    ba[depth]='A';dfs(depth+1);
    ba[depth]='B';dfs(depth+1);
}

int main(){
    n=rd();rd();
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)
     if(s[i]=='x') in[i]=++d;
    m=rd();
    for(int i=1;i<=m;i++){
        aa[i]=rd();a[i]=init();
        bb[i]=rd();b[i]=init();
    }
    dfs(1);
    if(!flag) printf("-1\n");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值