Codeforces Gym101190B. Binary Code(2-SAT前綴優化建圖)

N個命題至多成立一個的建圖

建立n個前綴命題i,pre[i]表示前i個命題存在一個成立

link(u,v)表示加邊   u>v 和 ~v > ~u

則只需要像這樣建邊即可:

for(int i = 0; i < n; i++) {
    if(i) {
        link(pre[i - 1], pre[i]);
        link(i, pre[i - 1] ^ 1);
    }
    link(i, pre[i]);
}

【題目】

給定一些二進制編碼,每個編碼至多有一個位置不知道是什麼。問是否存在一種補全編碼方式,使得沒有任何一個編碼是另一個編碼的前綴。

題目鏈接:https://codeforces.com/gym/101190

n,|s|\leq 5*10^{5}

【題解】

將所有編碼串插入字典樹中,其中不帶問號的串節點必選,帶問號的補爲0和1分別插入的串二選一。將選0和選1分別設置爲命題的真假。

問題轉化爲在Trie樹中任意節點到根節點的鏈上至多隻有一個節點被選中。

因爲一個節點上可能有多個串的結尾,還要保證每個節點上多個串最多隻選一個

代碼:

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

const int maxn = 4e6+5;

vector<int> G[maxn];
inline void add(int u,int v){ G[u].push_back(v); }
inline void link(int u,int v){ add(u, v), add(v^1, u^1);}
int low[maxn], dfn[maxn], stk[maxn], belong[maxn];
bool instack[maxn];
int idx, top, scc;
void tarjan(int u){
    low[u] = dfn[u] = ++idx;
    stk[top++] = u;
    instack[u] = true;
    for(int &v:G[u]){
        if(!dfn[v]){
            tarjan(v);
            if(low[u] > low[v]) low[u] = low[v];
        }else if(instack[v] && low[u] > dfn[v])
            low[u] = dfn[v];
    }
    if(low[u] == dfn[u]){
        scc++;
        for(;;){
            int v = stk[--top];
            instack[v] = false;
            belong[v] = scc;
            if(v == u) break;
        }
    }
}
int ch[maxn][2], tot;
int fa[maxn];
inline int insert(const char *s){
    int now = 0;
    for(int i = 0; s[i]; i++){
        if(!ch[now][s[i]-'0']) fa[ch[now][s[i]-'0'] = ++tot] = now;
        now = ch[now][s[i]-'0'];
    }
    return now;
}

int n;
string s[maxn];
int wh[maxn], pos[maxn];
vector<int> loc[maxn];

int main() {
#ifndef LOCAL
    freopen("binary.in", "r", stdin);
    freopen("binary.out", "w", stdout);
#endif // LOCAL
    cin>>n;
    for(int i = 1; i <= n; i ++){
        cin>>s[i];
        wh[i] = -1;
        for(int j = 0; j < s[i].size(); j++)
            if(s[i][j] == '?') wh[i] = j;
        if(wh[i] == -1){
            pos[i<<1] = pos[i<<1|1] = insert(s[i].c_str());
            add(i<<1|1,i<<1);//f -> t
        }else{
            s[i][wh[i]] = '0'; pos[i<<1] = insert(s[i].c_str());
            s[i][wh[i]] = '1'; pos[i<<1|1] = insert(s[i].c_str());
        }
    }
    int S = (n<<1|1)+1;
    for(int i = 1; i <= tot; i++)
        link(S + (fa[i]<<1), S + (i<<1));
    for(int i = 1; i <= n; i++){
        if(wh[i] == -1){
            loc[pos[i<<1]].push_back(i<<1);
            link(i<<1, S+(fa[pos[i<<1]]<<1|1));
            link(i<<1, S+(pos[i<<1]<<1));
        }else{
            loc[pos[i<<1]].push_back(i<<1);
            loc[pos[i<<1|1]].push_back(i<<1|1);
            link(i<<1, S+(fa[pos[i<<1]]<<1|1));
            link(i<<1, S+(pos[i<<1]<<1));
            link(i<<1|1, S+(fa[pos[i<<1|1]]<<1|1));
            link(i<<1|1, S+(pos[i<<1|1]<<1));
        }
    }
    S += (tot<<1|1) + 1;
    for(int i = 1; i <= tot; i++){
        if(!loc[i].size()) continue;
        for(int j = 0; j < loc[i].size(); j++){
            if(j){
                link(S+(j-1<<1), S+(j<<1));
                link(loc[i][j], S + ((j-1<<1)|1));
            }
            link(loc[i][j], S + (j<<1));
        }
        S += (loc[i].size()<<1);
    }
    for(int i = 0; i <= S; i++)
        if(!dfn[i]) tarjan(i);
    for(int i = 1; i <= n; i++)
        if(belong[i<<1] == belong[i<<1|1]) return puts("NO"),0;
    puts("YES");
    for(int i = 1; i <= n; i++){
        if(wh[i] != -1)
            s[i][wh[i]] = (belong[i<<1]<belong[i<<1|1]?'0':'1');
        cout<<s[i]<<endl;
    }
    return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章