LOJ#6036. 「雅礼集训 2017 Day4」编码(2-SAT)

传送门题解: 对于每个串,在Trie树上找到他’?’处填0或者1的位置。 那么他的子树中(不包括他)的所有串不能选。 对于他这个节点我们记录一个前缀后缀就可以做到O(n)O(n)O(n)了。#include <bits/stdc++.h>using namespace std;const int N=1e6+50;typedef pair <int,int&...
摘要由CSDN通过智能技术生成

传送门

题解:
对于每个串,在Trie树上找到他’?’处填0或者1的位置。 那么他的子树中(不包括他)的所有串不能选。 对于他这个节点我们记录一个前缀后缀就可以做到 O(n) O ( n ) 了。

#include <bits/stdc++.h>
using namespace std;
const int N=1e6+50;

typedef pair <int,int> pii;
int n; string s[N/2];
int son[N][2],any[N/2],tot=1,cnt=0;
pii pos[N/2][2];
int u[N/2][2],v[N][2];
vector <int> cont[N];
vector <int> pre[N][2];
vector <int> suf[N][2];
int dfn[N*5],ind,g[N*5],nt[N*10],vt[N*10],st[N*5],top,ins[N*5],low[N*5],id[N*5],ec,scc;
inline void add(int x,int y) {nt[++ec]=g[x]; g[x]=ec; vt[ec]=y;}
inline void addbi(int x,int y) {add(x,y); add(y,x);}

inline int find_(string &t,int p=0) {
    for(;p<t.length();++p) if(t[p]=='?') break;
    return p;
}

inline pii inc(int id,string &t) {
    int p=1;
    for(int i=0;i<t.length();i++) {
        int c=t[i]-'0';
        if(!son[p][c]) son[p][c]=++tot;
        p=son[p][c];
    }
    cont[p].push_back(id);
    return pii(p,cont[p].size()-1);
}

inline void build_graph() {
    for(int i=1;i<=n;i++) u[i][0]=++cnt, u[i][1]=++cnt;
    for(int i=1;i<=tot;i++) v[i][0]=++cnt, v[i][1]=++cnt;
    for(int i=1;i<=tot;i++) {
        pre[i][0].resize(cont[i].size()); 
        pre[i][1].resize(cont[i].size()); 
        suf[i][0].resize(cont[i].size()); 
        suf[i][1].resize(cont[i].size()); 
        for(int j=0;j<cont[i].size();++j)
            pre[i][0][j]=++cnt, pre[i][1][j]=++cnt,
            suf[i][0][j]=++cnt, suf[i][1][j]=++cnt;
    }
    for(int i=1;i<=tot;i++) {
        for(int j=0;j<=1;j++) if(son[i][j]) {
            int t=son[i][j];
            add(v[i][0],v[t][0]);
            add(v[t][1],v[i][1]);
        }
        for(int j=0;j<cont[i].size();++j) {
            int o=cont[i][j]; if(o>n) o-=n;
            int t1=u[o][0], t2=u[o][1];
            if(cont[i][j]>n) swap(t1,t2);
            if(j>0) add(pre[i][0][j],pre[i][0][j-1]);
            add(pre[i][0][j],t2);
            if(j>0) add(pre[i][1][j-1],pre[i][1][j]);
            add(t1,pre[i][1][j]);

            if(j<cont[i].size()-1) add(suf[i][0][j],suf[i][0][j+1]);
            add(suf[i][0][j],t2);
            if(j<cont[i].size()-1) add(suf[i][1][j+1],suf[i][1][j]);
            add(t1,suf[i][1][j]);

            if(j==0) {
                addbi(v[i][1],suf[i][1][j]);
                addbi(v[i][0],suf[i][0][j]);
            }
            if(j==cont[i].size()-1) {
                addbi(v[i][1],pre[i][1][j]);
                addbi(v[i][0],pre[i][0][j]);
            }
        }
    }

    for(int i=1;i<=n;i++) {
        int p1=pos[i][0].first, p2=pos[i][0].second;
        for(int j=0;j<=1;j++) if(son[p1][j]) {
            add(u[i][0],v[son[p1][j]][0]);
            add(v[son[p1][j]][1],u[i][1]);
        }
        if(p2) 
            add(u[i][0],pre[p1][0][p2-1]), 
            add(pre[p1][1][p2-1],u[i][1]);
        if(p2<cont[p1].size()-1)
            add(u[i][0],suf[p1][0][p2+1]),
            add(suf[p1][1][p2+1],u[i][1]);

        p1=pos[i][1].first, p2=pos[i][1].second;
        if(p1) {
            for(int j=0;j<=1;j++) if(son[p1][j]) {
                add(u[i][1],v[son[p1][j]][0]);
                add(v[son[p1][j]][1],u[i][0]);
            }
            if(p2)
                add(u[i][1],pre[p1][0][p2-1]),
                add(pre[p1][1][p2-1],u[i][0]);
            if(p2<cont[p1].size()-1)
                add(u[i][1],suf[p1][0][p2+1]),
                add(suf[p1][1][p2+1],u[i][0]);
        } else add(u[i][1],u[i][0]);
    }
}

inline void dfs(int x) {
    dfn[x]=low[x]=++ind;
    st[++top]=x; ins[x]=1;
    for(int e=g[x];e;e=nt[e]) {
        if(!dfn[vt[e]]) dfs(vt[e]), low[x]=min(low[x],low[vt[e]]);
        else if(ins[vt[e]]) low[x]=min(low[x],dfn[vt[e]]);
    }
    if(low[x]==dfn[x]) {
        int u; ++scc; do {
            ins[u=st[top--]]=0;
            id[u]=scc;
        } while(u!=x);
    }
}


int main() {
    ios::sync_with_stdio(false); 
    cin.tie(NULL); cout.tie(NULL);

    cin>>n;
    for(int i=1;i<=n;i++) {
        cin>>s[i];
        int t=find_(s[i]); any[i]=t;
        if(t==s[i].length()) pos[i][0]=inc(i,s[i]);
        else {
            s[i][t]='0'; pos[i][0]=inc(i,s[i]);
            s[i][t]='1'; pos[i][1]=inc(i+n,s[i]);
            s[i][any[i]]='?';
        }
    }
    build_graph();
    for(int i=1;i<=cnt;i++) if(!dfn[i]) dfs(i);
    for(int i=1;i<=n;i++) {
        if(id[u[i][0]]==id[u[i][1]]) {puts("NO"); return 0;}
        if(id[u[i][0]]<id[u[i][1]]) {
            if(any[i]<s[i].length()) 
                s[i][any[i]]='0';
        } else s[i][any[i]]='1';
    }
    cout<<"YES"<<'\n';
//    for(int i=1;i<=n;i++) cout<<s[i]<<'\n';
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值