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
using namespace std;

const int maxn = 4e6+5;

vector 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 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]

 

你可能感兴趣的:(2-sat)