[NOI2017]游戏(2SAT)

这是约半年前写的题解了,就搬过来吧

感觉这是NOI2017最水的一题(当然我还是不会2333),因为是一道裸的2-SAT。我就是看着这道题学的2-SAT

算法一:暴力枚举。对于abc二进制枚举,对于x则采用三进制枚举即可,复杂度O(3^d*2^(n-d)),再进行适当剪支,期望得分:40~50

算法二:对于d=0的点,采用2-SAT算法,这是最裸的2-SAT,这样,复杂度为O(n+m),综合算法一,期望得分为60

算法三:不妨对于d个x,采用三进制枚举,枚举a/b/c,再做裸的2-SAT即可,复杂度O(3^d*(n+m)),期望得分:75~90,如果常数小可以X过去

算法四:不难发现,三进制枚举不但效率慢,而且难写、没必要,对于判断会很复杂,不如只枚举禁止使用A/B,因为这样已经包含了A/B/C三种赛车,所以可以这么做(因为题目只要求一组解),复杂度O(2^d*(n+m)),期望得分:100

难点:2-SAT和输出方案的2-SAT

注意:选A就选B等价于选B'就选A’,所以需要连接双向边!!

不说了上code

#include<bits/stdc++.h>
#define mem(x) memset(x,0,sizeof x)
using namespace std;
const int N=2e5+99;
int n,m,d,scc,cnt,top,c[N],s1[N],t1[N],dfn[N],low[N],part[N],hd[N],v[N],nxt[N],q[N];
char s[N],s2[N],t2[N],t[N];
bool vis[N],flag;
void add(int x,int y){v[++cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt;}
int opp(int x){return x>n?x-n:x+n;}
int jisuan(int x,char ch)
{
    if(s[x]=='a')return ch=='B'?x:x+n;
    if(s[x]=='b'||s[x]=='c')return ch=='A'?x:x+n;
    if(ch=='C')return x+n;
    return x;
}
void tarjan(int u)
{
    dfn[u]=low[u]=++cnt;
    q[++top]=u;
    vis[u]=1;
    for(int i=hd[u];i;i=nxt[i])
    if(!dfn[v[i]])
    {
        tarjan(v[i]);
        low[u]=min(low[u],low[v[i]]);
    }
    else if(vis[v[i]])low[u]=min(low[u],dfn[v[i]]);
    if(dfn[u]==low[u])
    {
        part[u]=++scc;
        vis[u]=0;
        int now;
        do{
            now=q[top--];
            vis[now]=0;
            part[now]=scc;
        }while(now!=u);
    }
}
void solve()
{
    scc=cnt=0;
    mem(dfn);mem(low);mem(part);mem(hd);mem(nxt);
    for(int i=1;i<=m;i++)
    if(s[s1[i]]!='x'&&s[t1[i]]!='x'&&s[s1[i]]-32!=s2[i])
    {
        int u=jisuan(s1[i],s2[i]),v;
        if(s[t1[i]]-32==t2[i])add(u,opp(u));
        else{
            v=jisuan(t1[i],t2[i]);
            add(u,v);add(opp(v),opp(u));
        }
    }
    else{
        char S=s[s1[i]],T=s[t1[i]];
        int x=c[s1[i]],y=c[t1[i]];
        if(S=='x'&&T=='x'&&s2[i]!=t[x])
        {
            int u=jisuan(s1[i],s2[i]),v;
            if(t2[i]==t[y])add(u,opp(u));
            else{
                v=jisuan(t1[i],t2[i]);
                add(u,v);add(opp(v),opp(u));
            }
        }
        else if(S=='x'&&T!='x'&&s2[i]!=t[x])
        {
            int u=jisuan(s1[i],s2[i]),v;
            if(s[t1[i]]-32==t2[i])add(u,opp(u));
            else{
                v=jisuan(t1[i],t2[i]);
                add(u,v);add(opp(v),opp(u));
            }
        }
        else if(S!='x'&&T=='x'&&s[s1[i]]-32!=s2[i])
        {
            int u=jisuan(s1[i],s2[i]),v;
            if(t2[i]==t[y])add(u,opp(u));
            else{
                v=jisuan(t1[i],t2[i]);
                add(u,v);add(opp(v),opp(u));
            }
        }
    }
    cnt=0;
    for(int i=1;i<=n*2;i++)if(!dfn[i])tarjan(i);
    for(int i=1;i<=n;i++)if(part[i]==part[i+n])return;
    for(int i=1;i<=n;i++)
    if(part[i]<part[i+n])
    {
        if(s[i]=='a')printf("B");
        else if(s[i]=='b'||s[i]=='c')printf("A");
        else if(t[c[i]]=='A')printf("B");
        else printf("A");
    }
    else{
        if(s[i]=='a'||s[i]=='b')printf("C");
        else if(s[i]=='c')printf("B");
        else if(t[c[i]]=='A')printf("C");
        else printf("B");
    }
    flag=1;
}
void fact(int k)
{
    if(flag)return;
    if(k>d){solve();return;}
    t[k]='A';fact(k+1);t[k]='B';fact(k+1);
}
int main()
{
    scanf("%d%d",&n,&d);
    d=0;
    scanf("%s",s+1);
    for(int i=1;i<=n;i++)if(s[i]=='x')c[i]=++d;
    scanf("%d",&m);
    for(int i=1;i<=m;i++)scanf("%d %c%d %c",&s1[i],&s2[i],&t1[i],&t2[i]);
    fact(1);
    if(!flag)puts("-1");
}
View Code
原文地址:https://www.cnblogs.com/hfctf0210/p/10155788.html