[(可持久化)字典树 优化建图][2-SAT] LOJ#6036. && 雅礼集训 2017 Day4. 编码

老早以前的坑了
貌似好多地方都有这个题
因为每个串都只有一个问号,问号可取0可取1,这就是一个经典的2-SAT模型
但是直接做的话,边数是 n2 级别的,不过因为是01串,可以用可持久化字典树优化建图(可能也可以不可持久化,但是我觉得可持久化比较方便)

UPD:代码可能是假的,看http://blog.csdn.net/coldef/article/details/78303359

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <string>
#include <cstring>
#include <map>
#include <ctime>
#include <vector>
#include <stack>
#define N 2000010

using namespace std;

int n,cnt,g,flg;
int B[N],L[N],R[N],dfn[N],low[N],vis[N],wh[N],len[N],G[N];
char *A[N];
char M[N];
stack<int> S;
struct edge{
  int t,nx;
}E[N];

inline void Insert(int x,int y){
  E[++cnt].t=y; E[cnt].nx=G[x]; G[x]=cnt;
}
map<string,int> Mp;

struct trie{
  int node[N][3],cnt,enin[N];
  trie(){ cnt=1; }
  void insert(char *a,int x,int len){
    int k=1,i=0;
    while(i<len){
      if(node[k][a[i]-'0']) k=node[k][a[i]-'0'];
      else k=node[k][a[i]-'0']=++cnt;
      i++;
    }
    enin[k]=1;
  }
  bool check(char *a,int len){
    int k=1,i=0;
    while(i<len){
      if(node[k][a[i]-'0']) k=node[k][a[i]-'0']; else return false;
      if(enin[k]) return true;
      i++;
    }
    return true;
  }
}C;

struct itrie{
  int node[N][3],cnt,enin[N],G[N],g;
  itrie(){ cnt=1;}
  void insert(char *a,int x,int len){
    int k=1,i=0;
    while(i<len){
      if(node[k][a[i]-'0']) Insert(node[k][a[i]-'0']+g,cnt+1+g);
      k=node[k][a[i]-'0']=++cnt;
      i++;
    }
    Insert(k+g,x);
    enin[k]=1;
  }
  void explore(char *a,int x,int len){
    int k=1,i=0,w=0;
    while(i<len){
      k=node[k][a[i]-'0'];
      if(enin[k]) w=k;
      i++;
    }
    if(w) Insert(x,w+g);
  }
}D,F;

void tarjan(int x){
  low[x]=dfn[x]=++g; vis[x]=1;
  S.push(x);
  for(int i=G[x];i;i=E[i].nx){
    if(!vis[E[i].t]){
      tarjan(E[i].t);
      low[x]=min(low[E[i].t],low[x]);
    }
    else if(vis[E[i].t]==1) low[x]=min(low[x],dfn[E[i].t]);
  }
  if(low[x]==dfn[x]){
    ++flg;
    while(1){
      int w=S.top(); S.pop();
      vis[w]=2;
      wh[w]=flg;
      if(w==x||S.empty()) break;
    }
  }
}

int main(){
  scanf("%d",&n);
  for(int i=1,j;i<=n;i++){
    scanf("%s",M); len[i]=strlen(M);
    int tot=++Mp[M];
    if(tot>3) return puts("NO"),0;
    A[i]=new char[strlen(M)+5];
    for(j=0;j<len[i];j++) A[i][j]=M[j];
    for(j=0;j<len[i];j++) if(A[i][j]=='?') break;
    if(j==len[i]) B[i]=-1;
    else B[i]=j;
  }
  for(int i=1;i<=n;i++)
    if(B[i]==-1) if(C.check(A[i],len[i])) return puts("NO"),0;
      else C.insert(A[i],i,len[i]);
  for(int i=1;i<=n;i++)
    if(B[i]!=-1){
      int k=0;
      A[i][B[i]]='0';
      if(C.check(A[i],len[i])) k+=1;
      A[i][B[i]]='1';
      if(C.check(A[i],len[i])) k+=2;
      if(k==3) return puts("NO"),0;
      if(!k) continue;
      if(k==1)
    A[i][B[i]]='1',C.insert(A[i],i,len[i]);
      else
    A[i][B[i]]='0',C.insert(A[i],i,len[i]);
      B[i]=-1;
    }
  D.g=500000; F.g=1000000;
  for(int i=1;i<=n;i++)
    if(B[i]!=-1){
      A[i][B[i]]='0';
      D.explore(A[i],i<<1|1,len[i]);
      A[i][B[i]]='1';
      D.explore(A[i],i<<1,len[i]);
      A[i][B[i]]='0';
      D.insert(A[i],i<<1,len[i]);
      A[i][B[i]]='1';
      D.insert(A[i],i<<1|1,len[i]);
    }
  for(int i=n;i;i--)
    if(B[i]!=-1){
      A[i][B[i]]='0';
      F.explore(A[i],i<<1|1,len[i]);
      A[i][B[i]]='1';
      F.explore(A[i],i<<1,len[i]);
      A[i][B[i]]='0';
      F.insert(A[i],i<<1,len[i]);
      A[i][B[i]]='1';
      F.insert(A[i],i<<1|1,len[i]);
    }
  for(int i=2;i<=(n<<1|1);i++)
    if(!vis[i]) tarjan(i);
  for(int i=1;i<=n;i++)
    if(wh[i<<1]==wh[i<<1|1]) return puts("NO"),0;
  puts("YES");
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值