首页 > 技术文章 > Luogu P3825 [NOI2017]游戏(2-SAT)

coder-Uranus 2018-11-02 19:30 原文

P3825 [NOI2017]游戏

题意

题目背景

狂野飙车是小\(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"(不含双引号)。

输入输出格式

输入格式:

输入第一行包含两个非负整数\(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\),含义见题目描述。

输出格式:

输出一行。

若无解输出"-1"(不含双引号)。

若有解,则包含一个长度为\(n\)的仅包含大写字母\(A,B,C\)的字符串,表示小\(L\)在这\(n\)场游戏中如何安排赛车的使用。如果存在多组解,输出其中任意一组即可。

因为\(spacial\ judge\),最后一行不要输出回车。

输入输出样例

输入样例#1:

3 1
xcc
1
1 A 2 B

输出样例#1:

ABA

说明

【样例1解释】

\(L\)计划进行\(3\)场游戏,其中第\(1\)场的地图类型是\(x\),适合所有赛车,第\(2\)场和第\(3\)场的地图是\(c\),不适合赛车\(C\)

\(L\)希望:若第\(1\)场游戏使用赛车\(A\),则第\(2\)场游戏使用赛车\(B\)。那么为这\(3\)场游戏分别安排赛车\(A,B,A\)可以满足所有条件。若依次为\(3\)场游戏安排赛车为\(BBB\)\(BAA\)时,也可以满足所有条件,也被视为正确答案。但依次安排赛车为\(AAB\)\(ABC\)时,因为不能满足所有条件,所以不被视为正确答案。

P3825

思路

这道水题怎么还是黑的? --huyufeifei

调了一晚上这道题,真的毒瘤。

我们先不考虑\(x\)地图,那么剩余的地图每种只有两辆赛车可跑。再根据题目给的约束条件,我们就可以用\(2-SAT\)来解决这个问题。

那么带上\(x\)地图怎么办呢?既然\(d\)很小,那我们就暴力枚举!把\(x\)地图强行换成\(a,b,c\)中的一张,再来判断是否可行。最终复杂度为\(O(3^dm)\),显然很爆炸。考虑到如果我们只枚举\(a,b\),其实就可以覆盖\(x\)地图可以使用\(A,B,C\)三种赛车的条件,那就不枚举\(c\)好了。最终时间复杂度\(O(2^dm)\)

我在做这题时出现了很多问题,调试了很久,枚举如下:

  • 因为每张地图对应的赛车不同,所以不同地图在\(2-SAT\)中的两个取值完全不同,要注意区分。
  • 建边时要建两条边。对于如果\(a\)那么\(b\)这个条件,不能只建边\((a,b)\),还要建边\((b+n,a+n)\)(想一想为什么)。
  • 暴力枚举不要打挂(没错,我因为打挂了暴力调了好久)。

AC代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const LL MAXN=2e5+5,MAXM=4e5+5;
LL n,d,m,tot,js,dfn[MAXN],low[MAXN],bel[MAXN];
LL x[MAXM],y[MAXM];
LL cnt,top[MAXN],to[MAXM],nex[MAXM];
string str;
char xx[MAXM],yy[MAXM];
bool vis[MAXN];
stack<LL>S;
LL read()
{
    bool f=true;LL re=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=false;ch=getchar();}
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return f?re:-re;
}
void add_edge(LL x,LL y){to[++cnt]=y,nex[cnt]=top[x],top[x]=cnt;}
char fst(char y)
{
    if(y=='A') return 'B';
    else if(y=='B') return 'C';
    else if(y=='C') return 'A';
}
char scd(char y)
{
    if(y=='A') return 'C';
    else if(y=='B') return 'A';
    else if(y=='C') return 'B';
}
void tarjan(LL now)
{
    dfn[now]=low[now]=++tot,vis[now]=true,S.push(now);
    for(LL i=top[now];i;i=nex[i])
        if(!dfn[to[i]]) tarjan(to[i]),low[now]=min(low[now],low[to[i]]);
        else if(vis[to[i]]) low[now]=min(low[now],dfn[to[i]]);
    if(dfn[now]==low[now])
    {
        bel[now]=++js,vis[now]=false;
        while(S.top()!=now) bel[S.top()]=js,vis[S.top()]=false,S.pop();
        S.pop();
    }
}
bool check()
{
    cnt=tot=js=0;
    memset(dfn,0,sizeof dfn);
    memset(top,0,sizeof top);
    for(LL i=1;i<=m;i++)
    {
        char f1=fst(str[x[i]]),s1=scd(str[x[i]]),f2=fst(str[y[i]]),s2=scd(str[y[i]]);
        if(xx[i]==str[x[i]]) continue;
        else if(yy[i]==str[y[i]])
        {
            if(xx[i]==f1) add_edge(x[i],x[i]+n);
            else if(xx[i]==s1) add_edge(x[i]+n,x[i]);
        }
        else if(xx[i]==f1&&yy[i]==f2) add_edge(x[i],y[i]),add_edge(y[i]+n,x[i]+n);
        else if(xx[i]==f1&&yy[i]==s2) add_edge(x[i],y[i]+n),add_edge(y[i],x[i]+n);
        else if(xx[i]==s1&&yy[i]==f2) add_edge(x[i]+n,y[i]),add_edge(y[i]+n,x[i]);
        else if(xx[i]==s1&&yy[i]==s2) add_edge(x[i]+n,y[i]+n),add_edge(y[i],x[i]);
    }
    for(LL i=1;i<=(n<<1);i++) if(!dfn[i]) tarjan(i);
    for(LL i=1;i<=n;i++) if(bel[i]==bel[i+n]) return false;
    for(LL i=1;i<=n;i++)
        if(bel[i]<bel[i+n]) putchar(fst(str[i]));
        else putchar(scd(str[i]));
    return true;
}
bool dfs(LL now,LL hjj)
{
    if(hjj==d)
    {
        if(check()) return true;
        return false;
    }
    while(str[now]!='X') now++;
    str[now]='A';
    if(dfs(now+1,hjj+1)) return true;
    str[now]='B';
    if(dfs(now+1,hjj+1)) return true;
    str[now]='X';
    return false;
}
int main()
{
    cin>>n>>d;
    cin>>str;str=" "+str;
    for(LL i=1;i<=n;i++) str[i]=toupper(str[i]);
    cin>>m;
    for(LL i=1;i<=m;i++) cin>>x[i]>>xx[i]>>y[i]>>yy[i];
    if(!dfs(1,0)) printf("-1");
    return 0;
}

推荐阅读