loj#6036 编码

分析

考虑trie+2sat

每次将?=0和?=1的分别插入

插入串时将这个点的选择状态和前缀的选择状态连关系边

注意串结束时建一个新点表示当前串

最后跑2sat即可

代码

#include<bits/stdc++.h>
using namespace std;
#define a(x) x<<1
#define b(x) x<<1|1
#define pb push_back
stack<int>a;
string s[500100];
vector<int>v[3000100];
int id[500100],siz[500100],n,m;
int trie[3000100][2],cnt,bel[3000100];
int low[3000100],dfn[3000100],is[3000100],T,sum;
inline bool cmp(int a,int b){return siz[a]<siz[b];}
inline void add(int x,int y){v[x].pb(y),v[y^1].pb(x^1);}
inline void tarjan(int x,int fa){
    dfn[x]=low[x]=++T;
    a.push(x);
    is[x]=1;
    for(int i=0;i<v[x].size();i++)
      if(v[x][i]!=fa){
          if(!dfn[v[x][i]]){
            tarjan(v[x][i],x);
            low[x]=min(low[x],low[v[x][i]]);
          }else if(is[v[x][i]]){
            low[x]=min(low[x],dfn[v[x][i]]);
          }
      }
    if(low[x]==dfn[x]){
      sum++;
      while(1){
          int u=a.top();
          a.pop();
          is[u]=0;
          bel[u]=sum;
          if(u==x)break;
      }
    }
    return;
}
inline void ins(int i,int x){
    int p=0,wh,la=0;
    for(int j=0;j<siz[i];j++){
      wh=(s[i][j]-'0');
      if(!trie[p][wh])trie[p][wh]=++cnt;
      la=p,p=trie[p][wh],add(x,a(p));
    }
    trie[la][wh]=++cnt;
    add(x,b(cnt)),add(a(cnt),a(p));
    return;
}
int main(){
    int i,j,k,ok;
    scanf("%d",&n);cnt=n;
    for(i=1;i<=n;i++){
      cin>>s[i];
      id[i]=i;
      siz[i]=s[i].length();
    }
    sort(id+1,id+n+1,cmp);
    for(int _=1;_<=n;_++){
      i=id[_],ok=0;
      for(j=0;j<siz[i];j++)
        if(s[i][j]=='?'){
          s[i][j]='0',ins(i,a(i));
          s[i][j]='1',ins(i,b(i));
          ok=1;
          break;
        }
      if(!ok){
          ins(i,a(i));
          add(b(i),a(i));
      }
    }
    for(i=2;i<=(cnt<<1|1);i++)if(!dfn[i])tarjan(i,0);
    for(i=1;i<=cnt;i++)
      if(bel[a(i)]==bel[b(i)]){
          puts("NO");
          return 0;
      }
    puts("YES");
    return 0;
}

 

posted @ 2019-10-14 07:41  水题收割者  阅读(180)  评论(0编辑  收藏  举报