zoukankan      html  css  js  c++  java
  • LOJ6036 「雅礼集训 2017 Day4」 编码 2-SAT、Trie

    传送门


    每个串只有一个??还只能填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] ightarrow bool[i][1])

    (bool[i][0] ightarrow bool[i -1][0])

    (bool[i][1] ightarrow bool[i + 1][1])

    (bool[i][0] ightarrow belong[i][0])

    (bool[i][1] ightarrow 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;
    }
    
  • 相关阅读:
    Excel导出工具
    载入Properties文件中的配置项信息
    对list进行排序-重写排序规则
    Mysql 5.7版本报错 1055
    SVN客户端与服务端安装详解
    持续集成的理解
    js 日期格式化
    mysql多个时间戳字段默认值问题
    eclipse svn插件地址
    orientationchange移动端横竖屏切换属性
  • 原文地址:https://www.cnblogs.com/Itst/p/10467861.html
Copyright © 2011-2022 走看看