首页 > 其他 > 详细

LOJ6036 编码 2-SAT、Trie

时间:2019-03-03 21:54:02      阅读:246      评论:0      收藏:0      [点我收藏+]

传送门


每个串只有一个??还只能填0或者1,不难想到2-SAT求解。

一个很暴力的想法是枚举?0或者1,然后对所有可能的前缀连边。这样边数是\(O(n^2)\)的,需要优化。

看到前缀不难想到Trie树。将所有串的所有可能形态填入Trie树中,然后使用前缀后缀优化2-SAT连边的方式优化连边。

具体来说对于每一个串开两个点表示?0还是1,对于Trie树上每一个串的结束节点也开两个点,表示这个点及其所有前缀中是否存在已经选过的串。

连边考虑一些互为前缀的串。设串为\(s_1,s_2,s_3,...,s_k\),第\(i\)个串在Trie树上的节点的\(01\)变量为\(bool[i][0/1]\),第\(i\)个节点对应串的\(01\)变量为\(belong[i][0/1]\)(为了好描述,这里定义的\(belong[i][0/1]\)表示第\(i\)个串填入01之后是否得到当前串,是为\(1\)

那么有边

\(belong[i][1] \rightarrow bool[i][1]\)

\(bool[i][0] \rightarrow bool[i -1][0]\)

\(bool[i][1] \rightarrow bool[i + 1][1]\)

\(bool[i][0] \rightarrow belong[i][0]\)

\(bool[i][1] \rightarrow belong[i + 1][0]\)

这些边可以在建Trie的过程中直接建。记得要建逆否命题的边。然后跑一遍缩点就行了

细节:①开始要将字符串按长度从小到大排序,才可以保证上面方法的正确性;②可能存在某些串相等,建Trie的时候要特别注意。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<ctime>
#include<algorithm>
#include<cstring>
#include<iomanip>
#include<queue>
#include<vector>
#define INF 0x3f3f3f3f
//This code is written by Itst
using namespace std;

const int MAXN = 3e6 + 3;
struct Edge{
    int end , upEd;
}Ed[MAXN << 3];
int head[MAXN] , N , cntN = 1 , cntEd;

inline void addEd(int a , int b){
    Ed[++cntEd] = (Edge){b , head[a]};
    head[a] = cntEd;
}

namespace Trie{
    int ch[MAXN][2] , ind[MAXN] , cnt = 1;

    void insert(string s , int bl){
    int cur = 1 , up = 0;
    for(auto c : s){
        if(!ch[cur][c - '0'])
        ch[cur][c - '0'] = ++cnt;
        cur = ch[cur][c - '0'];
        if(ind[cur]) up = ind[cur];
    }
    cntN += 2;
    addEd(bl , cntN); addEd(cntN ^ 1 , bl ^ 1);
    if(up){
        addEd(up , cntN); addEd(cntN ^ 1 , up ^ 1);
        addEd(up , bl ^ 1); addEd(bl , up ^ 1);
    }
    ind[cur] = cntN;
    }
}
using Trie::insert;

int stk[MAXN] , dfn[MAXN] , low[MAXN] , in[MAXN];
int top , ts , cntSCC;
bool vis[MAXN] , ins[MAXN];

void pop(int x){
    ++cntSCC;
    do{
    in[stk[top]] = cntSCC;
    ins[stk[top]] = 0;
    }while(stk[top--] != x);
}

void tarjan(int x , int p){
    vis[x] = ins[x] = 1;
    stk[++top] = x;
    dfn[x] = low[x] = ++ts;
    for(int i = head[x] ; i ; i = Ed[i].upEd){
    if(!vis[Ed[i].end]) tarjan(Ed[i].end , x);
    else if(!ins[Ed[i].end]) continue;
    low[x] = min(low[x] , low[Ed[i].end]);
    }
    if(dfn[x] == low[x]) pop(x);
}

vector < string > str;

bool cmp(string a , string b){return a.size() < b.size();}

int main(){
#ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    //freopen("out","w",stdout);
#endif
    cin >> N;
    for(int i = 1 ; i <= N ; ++i){
    string s;
    cin >> s;
    str.push_back(s);
    }
    sort(str.begin() , str.end() , cmp);
    for(auto t : str){
    cntN += 2;
    int nd = cntN , pos = t.find('?');
    if(pos != string::npos){
        t[pos] = '0';
        insert(t , nd - 1);
        t[pos] = '1';
        insert(t , nd);
    }
    else{
        insert(t , nd);
        addEd(nd - 1 , nd);
    }
    }
    for(int i = 2 ; i <= cntN ; ++i)
    if(!vis[i]) tarjan(i , 0);
    for(int i = 2 ; i <= cntN ; i += 2)
    if(in[i] == in[i + 1])
        return puts("NO") , 0;
    puts("YES");
    return 0;
}

LOJ6036 编码 2-SAT、Trie

原文:https://www.cnblogs.com/Itst/p/10467861.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!