如果不看 $x$ 赛场的话,剩下的三个赛场显然每个赛场只有两个赛车选择项,也就是说这样就变成了裸的 $2-sat$ ,直接做就好了。

如果加入有三个赛车选择项的 $x$ 赛场的话,显然就不好用 $2-sat$ 做了,难不成用 $3-sat$ ?

其实,如果 $x$ 只有两个赛车选择项的话一样是可以做的,我们考虑枚举每一个 $x$ 赛场所禁用的那个赛车,这样子就可以 $2-sat$ 了,不过这样 $dfs$ 的复杂度是 $3^d$ 的,爆炸了……

但是最终情况就是每一个 $x$ 赛场都要有过 $A,B,C$ 三种赛车的选择项,那么在禁掉 $A,B$ 的时候,剩下的赛车分别是 $B,C$ 和 $A,C$ ,这已经包含 $A,B,C$ 三种赛车了,所以我们的 $dfs$ 只有两个分支了…… 复杂度就是 $2^d$ ,过去了。

考虑 $2-sat$ 怎么做,首先对于一组限制,如果 $i$ 选了 $h_i$ ,$j$ 就必须选 $h_j$ 。反之如果 $j$ 没选 $h_j$ ,那么 $i$ 就一定不能选择 $h_i$ ,这样连边就好了。注意如果 $j$ 不能选 $h_j$ 的话,$i$ 直接连 $i+n$ 即可。输出方案的话,对于一个 $i$ 点,我们只要对缩点后的图中拓扑一下,然后看 $i$ 所在的强连通分量和 $i+n$ 所在的强连通分量谁拓扑序更小谁就是答案。当然 $tarjan$ 已经按照拓扑序来了…… 所以直接判就好了。

细节多,看代码。

Code:

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

const int N=1e5+2;
const int M=4e5+2;

char S[N],No[N];
int n,d,m,book,t[N],head[N],cnt;
struct Edge{int nxt,to;} G[M];
struct Stint{int i,j;char hi,hj;} E[M];
int bel[N],dfn[N],low[N],hep[N],siz[N],tot,top,now;

void add(int u,int v) {
    G[++cnt]=(Edge){head[u],v},head[u]=cnt;
}

template <typename _Tp> inline void IN(_Tp&x) {
    char ch;bool flag=0;x=0;
    while(ch=getchar(),!isdigit(ch)) if(ch=='-') flag=1;
    while(isdigit(ch))  x=x*10+ch-'0',ch=getchar();
    if(flag) x=-x;
}

void Tarjan(int u) {
    low[u]=dfn[u]=++now,hep[++top]=u;
    for(int i=head[u];i;i=G[i].nxt) {
        int v=G[i].to;
        if(!dfn[v]) Tarjan(v),low[u]=min(low[u],low[v]);
        else if(!bel[v]) low[u]=min(low[u],dfn[v]);
    }
    if(!(low[u]^dfn[u])) {
        bel[u]=++tot,siz[tot]=1;
        while(hep[top]^u)
            ++siz[tot],bel[hep[top--]]=tot;
        --top;
    }return;
}

int opposite(int u) {return u>n?u-n:u+n;}
int point_id(int u,char c) {
    /*
        点的编号的规则就是对于一个赛场,除去禁用的赛车
        后,剩下的赛车小的为 i,大的为 i+n.
    */
    if(S[u]=='a') return c=='B'?u:n+u;
    if(S[u]=='b'||S[u]=='c') return c=='A'?u:n+u;
    return c=='C'?u+n:u;
}
bool sol() {
    cnt=tot=now=0;
    for(int i=1;i<=(n<<1);++i) bel[i]=head[i]=dfn[i]=0;
    for(int i=1;i<=m;++i)
        if(S[E[i].i]!='x'&&S[E[i].j]!='x') {
            /*如果 E[i].i 本身就不可以选择对应赛车的话直接跳*/
            if(S[E[i].i]-32==E[i].hi) continue;
            int u=point_id(E[i].i,E[i].hi);/*得到点编号*/
            /*如果 E[i].j 不可以选择对应赛车的话那么 u 无解*/
            if(S[E[i].j]-32==E[i].hj) {add(u,opposite(u));continue;}
            int v=point_id(E[i].j,E[i].hj);
            /*按要求连边*/
            add(u,v),add(opposite(v),opposite(u));
        } else {
            char si=S[E[i].i],sj=S[E[i].j];
            if(si=='x'&&sj=='x') {
                /*
                    注意这里禁掉的赛车变了,x 的那一方就判断一下当前
                    dfs 无法使用的赛车,其他的按照上面即可
                */
                if(No[t[E[i].i]]==E[i].hi) continue;
                int u=point_id(E[i].i,E[i].hi);
                if(No[t[E[i].j]]==E[i].hj) {add(u,opposite(u));continue;}
                int v=point_id(E[i].j,E[i].hj);
                add(u,v),add(opposite(v),opposite(u));
            } else if(si=='x'&&sj!='x') {
                if(No[t[E[i].i]]==E[i].hi) continue;
                int u=point_id(E[i].i,E[i].hi);
                if(S[E[i].j]-32==E[i].hj) {add(u,opposite(u));continue;}
                int v=point_id(E[i].j,E[i].hj);
                add(u,v),add(opposite(v),opposite(u));
            } else if(si!='x'&&sj=='x') {
                if(S[E[i].i]-32==E[i].hi) continue;
                int u=point_id(E[i].i,E[i].hi);
                if(No[t[E[i].j]]==E[i].hj) {add(u,opposite(u));continue;}
                int v=point_id(E[i].j,E[i].hj);
                add(u,v),add(opposite(v),opposite(u));
            }
        }
    /*2-sat 基础不再赘述*/
    for(int i=1;i<=(n<<1);++i) if(!dfn[i]) Tarjan(i);
    for(int i=1;i<=n;++i) if(bel[i]==bel[i+n]) return false;
    for(int i=1,j=0;i<=n;++i)
        if(S[i]=='a') printf("%c",bel[i]<bel[i+n]?'B':'C');
        else if(S[i]=='b') printf("%c",bel[i]<bel[i+n]?'A':'C');
        else if(S[i]=='c') printf("%c",bel[i]<bel[i+n]?'A':'B');
        else if(S[i]=='x') {/*可以简洁点但是懒得改了 (QwQ)*/
            char c=No[++j]=='A'?'B':'A';
            printf("%c",bel[i]<bel[i+n]?c:'C');   
        }
    return true;
}

void dfs(int step) {
    if(step>d) {
        if(book) exit(0);
        else book=sol();
        return;
    } 
    No[step]='A',dfs(step+1),/*两个分支*/
    No[step]='B',dfs(step+1);
}

int main() {
    IN(n),IN(d),scanf("%s",S+1);
    for(int i=1,j=0;i<=n;++i) if(S[i]=='x') t[i]=++j;
    char op1[2],op2[2];
    IN(m);
    for(int i=1;i<=m;++i)
        scanf("%d%s%d%s",&E[i].i,op1,&E[i].j,op2),E[i].hi=op1[0],E[i].hj=op2[0];
    dfs(1);
    if(!book) puts("-1");/*无解*/
    return 0;
}
分类: 文章

Qiuly

QAQ

0 条评论

发表回复

Avatar placeholder

您的电子邮箱地址不会被公开。 必填项已用 * 标注