BZOJ 4945: [Noi2017]游戏 2-SAT

title

BZOJ 4945
LUOGU 3825
Description

狂野飙车是小 L 最喜欢的游戏。与其他业余玩家不同的是,小 L 在玩游戏之余,还精于研究游戏的设计,因此他有着与众不同的游戏策略。
小 L 计划进行n场游戏,每场游戏使用一张地图,小 L 会选择一辆车在该地图上完成游戏。
小 L 的赛车有三辆,分别用大写字母A、B、C表示。地图一共有四种,分别用小写字母x、a、b、c表示。其中,赛车A不适合在地图a上使用,赛车B不适合在地图b上使用,赛车C不适合在地图c上使用,而地图x则适合所有赛车参加。适合所有赛车参加的地图并不多见,最多只会有d张。
n 场游戏的地图可以用一个小写字母组成的字符串描述。例如:S=xaabxcbc表示小 L 计划进行8场游戏,其中第1场和第5场的地图类型是x,适合所有赛车,第2场和第3场的地图是a,不适合赛车A,第4场和第7场的地图是b,不适合赛车B,第6场和第8场的地图是c,不适合赛车C。
小 L 对游戏有一些特殊的要求,这些要求可以用四元组 \((i, h_i, j, h_j)\)来描述,表示若在第\(i\)场使用型号为\(h_i\)的车子,则第\(j\)场游戏要使用型号为\(h_j\)的车子。
你能帮小 L 选择每场游戏使用的赛车吗?如果有多种方案,输出任意一种方案。如果无解,输出 “-1’’(不含双引号)。

Input

输入第一行包含两个非负整数\(n, d\)
输入第二行为一个字符串\(S\)\(n, d, S\)的含义见题目描述,其中\(S\)包含\(n\)个字符,且其中恰好\(d\)个为小写字母\(x\)
输入第三行为一个正整数\(m\),表示有\(m\)条用车规则。接下来\(m\)行,每行包含一个四元组\(i, h_i, j, h_j\),其中\(i, j\)为整数,\(h_i, h_j\)为字符a、b或c,含义见题目描述。

Output

输出一行。
若无解输出 “-1’’(不含双引号)。
若有解,则包含一个长度为n的仅包含大写字母A、B、C的字符串,表示小 L 在这n场游戏中如何安排赛车的使用。如果存在多组解,输出其中任意一组即可。
因为spacial judge,最后一行不要输出回车。

Sample Input

3 1
xcc
1
1 A 2 B

Sample Output

ABA

HINT
【样例1解释】
小 L 计划进行3场游戏,其中第1场的地图类型是x,适合所有赛车,第2场和第3场的地图是c,不适合赛车C。
小 L 希望:若第1场游戏使用赛车A,则第2场游戏使用赛车B。那么为这3场游戏分别安排赛车A、B、A可以满足所有条件。若依次为3场游戏安排赛车为BBB或BAA时,也可以满足所有条件,也被视为正确答案。但依次安排赛车为AAB或ABC时,因为不能满足所有条件,所以不被视为正确答案。
在这里插入图片描述
详细信息解释:
1.score:QAQ:此测试点应该输出-1然而你输出的是别的
2.score:QWQ:此测试点应该输出-1然而你输出的是-XXX(就是负号对了后面错了)
3.score:pwp:此测试点应该输出-1并且你的答案是正确的
4.score:qwq:你的方案里出现了不是A,B,C的字符
5.score:qaq:你的方案在第x的图中用了不让用的车
6.score:pvp:你的方案不能满足第x个约束
7.score:qvq:你的答案正确,恭喜嘤嘤嘤
else:蛇皮judge没能正确读入应该读的东西……

analysis

刚看到题目,很像是 \(3-SAT\) 问题,但是 \(d\) 最大只有 \(8\),所以可以 \(3^8\) 枚举禁止选哪种赛车,又发现其实可以枚举禁止 A (可以选B,C)或者禁止 B (可以选A,C),这样的话,所有的情况其实都被讨论过了,复杂度就有了:\(O(2^n)\)

接下来,容易发现是一个 \(2-SAT\) 问题,跑一个 \(tarjan\) ,缩点建图,然后根据 \(belong[i]\) 的大小选择第一个还是第二个,总复杂度:\(O(2^nn)\)

code

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
 
char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}
 
template<typename T>inline void write(T x)
{
    if (!x) { putchar('0'); return ; }
    if (x<0) putchar('-'), x=-x;
    T num=0, ch[20];
    while (x) ch[++num]=x%10+48, x/=10;
    while (num) putchar(ch[num--]);
}
 
inline char get()
{
    char ch;
    while ((ch=getchar())^'A' && ch^'B' && ch^'C');
    return ch;
}
 
int ver[maxn<<1],Next[maxn<<1],head[maxn],len;
inline void add(int x,int y)
{
    ver[++len]=y,Next[len]=head[x],head[x]=len;
}
 
int dfn[maxn],low[maxn],id;
int Stack[maxn],top;
int belong[maxn],tot;
bool instack[maxn];
inline void tarjan(int x)
{
    dfn[x]=low[x]=++id;
    Stack[++top]=x;
    instack[x]=1;
    for (int i=head[x]; i; i=Next[i])
    {
        int y=ver[i];
        if (!dfn[y])
        {
            tarjan(y);
            low[x]=min(low[x],low[y]);
        }
        else if (instack[y])
            low[x]=min(low[x],dfn[y]);
    }
    if (low[x]==dfn[x])
    {
        int k;
        ++tot;
        do
        {
            k=Stack[top--];
            belong[k]=tot;
            instack[k]=0;
        } while (k!=x);
    }
}
 
int n,m,tmp;
inline int neg(int x)
{
    return x>n?x-n:x+n;
}
 
char s[maxn];
inline int trans(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;
}
 
char a2[maxn],b2[maxn],Orz[maxn];
int a1[maxn],b1[maxn],all[maxn];
inline bool solve()
{
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(belong,0,sizeof(belong));
    len=id=tot=0;
    for (int i=1; i<=m; ++i)
        if (s[a1[i]]^'x' && s[b1[i]]^'x')
        {
            if (a2[i]==s[a1[i]]-32) continue;
            int u=trans(a1[i],a2[i]);
            if (b2[i]==s[b1[i]]-32) { add(u,neg(u)); continue; }
            int v=trans(b1[i],b2[i]);add(u,v);
            add(neg(v),neg(u));//反向连边
        }
        else
        {
            char o=s[a1[i]],p=s[b1[i]];
            int x=all[a1[i]],y=all[b1[i]];
            if (o=='x' && p=='x')
            {
                if (a2[i]==Orz[x]) continue;
                int u=trans(a1[i],a2[i]);
                if (b2[i]==Orz[y]) { add(u,neg(u)); continue; }
                int v=trans(b1[i],b2[i]);add(u,v);
                add(neg(v),neg(u));
            }
            else if (o=='x' && p^'x')
            {
                if (a2[i]==Orz[x]) continue;
                int u=trans(a1[i],a2[i]);
                if (b2[i]==s[b1[i]]-32) { add(u,neg(u)); continue; }
                int v=trans(b1[i],b2[i]);add(u,v);
                add(neg(v),neg(u));
            }
            else
            {
                if (a2[i]==s[a1[i]]-32) continue;
                int u=trans(a1[i],a2[i]);
                if (b2[i]==Orz[y]) { add(u,neg(u)); continue; }
                int v=trans(b1[i],b2[i]);add(u,v);
                add(neg(v),neg(u));
            }
        }
    for (int i=1; i<=(n<<1); ++i)
        if (!dfn[i]) tarjan(i);
    for (int i=1; i<=n; ++i)
        if (belong[i]==belong[i+n]) return 0;
    for (int i=1; i<=n; ++i)
        if (belong[i]<belong[i+n])
        {
            if (s[i]=='a') putchar('B');
            else if (s[i]=='b' || s[i]=='c') putchar('A');
            else if (Orz[all[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 (Orz[all[i]]=='A') putchar('C');
            else putchar('B');
        }
    return 1;
}
 
int d;
bool flag;
inline void dfs(int dep)
{
    if (dep>d)
    {
        if (!flag) flag=solve();
        if (flag) exit(0);
        return ;
    }
    Orz[dep]='A',dfs(dep+1);
    Orz[dep]='B',dfs(dep+1);
}
 
int main()
{
    read(n);read(tmp);
    scanf("%s",s+1);
    for (int i=1; i<=n; ++i)
        if (s[i]=='x') all[i]=++d;
    read(m);
    for (int i=1; i<=m; ++i) read(a1[i]),a2[i]=get(),read(b1[i]),b2[i]=get();
    dfs(1);
    if (!flag) puts("-1");
    return 0;
}

转载于:https://www.cnblogs.com/G-hsm/p/11323324.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值