1. 程式人生 > >Luogu P3825 [NOI2017]遊戲(2-SAT)

Luogu P3825 [NOI2017]遊戲(2-SAT)

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;
}