#2-SAT,Tarjan,前后缀优化建图,Trie#洛谷 6965 LOJ 6036 [NEERC 2016] Binary Code

题目传送门


分析

其实就是针对前缀的关系建边转化为 2-SAT 判定和输出方案。
问题是怎么建边,考虑建 Trie,为了避免自己连向自己,将 Trie 复制一份
如果 y 是 x 的前缀,那么 x 连向第一棵 Trie,状态的父节点连向 neg(y)
如果 y 是 x 的前缀,那么 y 连向第二棵 Trie,状态的父节点连向 neg(x)
第一棵 Trie 儿子连父亲,第二棵 Trie 父亲连儿子,就实现了不同串的建边。
相同串就单独进行前后缀优化建图即可。


代码

#include <iostream>
#include <string>
#include <vector>
#include <stack>
using namespace std;
const int N=3000011; struct node{int y,next;}e[N<<3]; vector<int>ed[N]; stack<int>st;
int trie[N][2],rk[N],fat[N],tot,cnt,color,v[N],dfn[N],low[N],as[N],col[N],n,m,et;
void Insert(string s,int id){
    int p=1,len=s.length();
    for (int i=0;i<len;++i){
        if (!trie[p][s[i]-48]) fat[trie[p][s[i]-48]=++tot]=p;
        p=trie[p][s[i]-48];
    }
    ed[p].push_back(id),rk[id]=p;
}
void add(int x,int y){e[++et]=(node){y,as[x]},as[x]=et;}
void print(int p){
    if (p==1) return;
    print(fat[p]);
    if (trie[fat[p]][1]==p) cout<<1;
        else cout<<0;
}
void tarjan(int x){
    dfn[x]=low[x]=++cnt,v[x]=1,st.push(x);
    for (int i=as[x];i;i=e[i].next)
    if (!dfn[e[i].y]){
        tarjan(e[i].y);
        low[x]=min(low[x],low[e[i].y]);
    }else if (v[e[i].y]) low[x]=min(low[x],dfn[e[i].y]);
    if (dfn[x]==low[x]){
        int y; ++color;
        do{
            y=st.top(),v[y]=0;
            col[y]=color;
            st.pop();
        }while (y!=x);
    }
}
int main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>n,tot=1;
    if (n>33333){
        cout<<"NO";
        return 0;
    }
    for (int i=1;i<=n;++i){
        string s; cin>>s;
        int len=s.length(),pos=-1;
        for (int j=0;j<len;++j)
        if (s[j]=='?'){
            s[j]='0';
            Insert(s,i<<1);
            s[j]='1';
            Insert(s,i<<1|1);
            pos=j;
            break;
        }
        if (pos==-1){
            Insert(s,i<<1);
            rk[i<<1|1]=rk[i<<1];
            add(i<<1|1,i<<1);
        }
    }
    m=(n+tot)*2+1;
    for (int i=2;i<=tot;++i){
        add((i+n)<<1|1,(fat[i]+n)<<1|1);
        if (ed[i].size()>1){
            int siz=ed[i].size();
            add(m+1,ed[i][0]^1);
            for (int j=1;j<siz;++j){
                add(m+j+1,m+j);
                add(ed[i][j],m+j);
                add(m+j+1,ed[i][j]^1);
            }
            m+=siz;
            add(m+siz,ed[i][siz-1]^1);
            for (int j=siz-2;~j;--j){
                add(m+j+1,m+j+2);
                add(ed[i][j],m+j+2);
                add(m+j+1,ed[i][j]^1);
            }
            m+=siz;
        }
        add((fat[i]+n)<<1,(i+n)<<1);
    }
    for (int i=1;i<=n;++i){
        add((rk[i<<1]+n)<<1|1,i<<1|1),add(i<<1,(fat[rk[i<<1]]+n)<<1|1);
        add((rk[i<<1|1]+n)<<1|1,i<<1),add(i<<1|1,(fat[rk[i<<1|1]]+n)<<1|1);
        
        add(i<<1,(rk[i<<1]+n)<<1),add((fat[rk[i<<1]]+n)<<1,i<<1|1);
        add(i<<1|1,(rk[i<<1|1]+n)<<1),add((fat[rk[i<<1|1]]+n)<<1,i<<1);
    }
    for (int i=2;i<=m;++i) if (!dfn[i]) tarjan(i);
    for (int i=1;i<=n;++i)
    if (col[i<<1]==col[i<<1|1]){
        cout<<"NO";
        return 0;
    }
    cout<<"YES"<<'\n';
    for (int i=1;i<=n;++i,cout<<'\n')
    if (col[i<<1]<col[i<<1|1]) print(rk[i<<1]);
        else print(rk[i<<1|1]);
    return 0;
}
posted @ 2025-07-05 17:57  lemondinosaur  阅读(14)  评论(0)    收藏  举报