传送门


每个串只有一个??还只能填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的更多相关文章

  1. LOJ6036编码

    每个串拆成两个,都插入trie数. 把trie树建出来后,每一条从根到叶子的链上最多只能有一个变量为1. 这是个经典的前后缀优化2-sat建图的套路. 树上的做法也就是边dfs边做而已. #inclu ...

  2. 数据结构与算法简记--Trie树

    Trie树 概念 多叉树,节点为字符串中的单个字符. Trie 树的本质,就是利用字符串之间的公共前缀,将重复的前缀合并在一起. 将多个字符串按字符拆分插入Trie树,用于字符串查找,关键词提示等 举 ...

  3. [LOJ6029~6052]雅礼集训 2017 选做

    Link 代码可以在loj上看我的提交记录. Day 1 [LOJ6029]市场 对于一次除法操作,若区间内所有数的减少量均相同则可视作区间减法,否则暴力递归下去.显然一个线段树节点只会被暴力递归进去 ...

  4. 前端笔记之服务器&Ajax(上)服务器&PHP&数据交互&HTTP

    一.服务器 1.1 什么是服务器,做什么的? 服务器,就是放在机房中的电脑,和我们的电脑的区别在与服务器有固定的IP,服务器的安全性和稳定性相当的高;性能一般就可以了,但是CPU的性能要比普通的客户机 ...

  5. 【LOJ6036】编码(2-sat)

    [LOJ6036]编码(2-sat) 题面 LOJ 题解 很显然的一个暴力: 枚举每个串中的?是什么,然后把和它有前缀关系的串全部给找出来,不合法的连边处理一下,那么直接跑\(2-sat\)就做完了. ...

  6. 51Nod1601 完全图的最小生成树计数 Trie Prufer编码

    原文链接https://www.cnblogs.com/zhouzhendong/p/51Nod1601.html 题目传送门 - 51Nod1601 题意 题解 首先我们考虑如何求答案. 我们将所有 ...

  7. 萌新笔记——用KMP算法与Trie字典树实现屏蔽敏感词(UTF-8编码)

    前几天写好了字典,又刚好重温了KMP算法,恰逢遇到朋友吐槽最近被和谐的词越来越多了,于是突发奇想,想要自己实现一下敏感词屏蔽. 基本敏感词的屏蔽说起来很简单,只要把字符串中的敏感词替换成"* ...

  8. 【LOJ6036】 「雅礼集训 2017 Day4」编码

    传送门 LOJ Solution 因为?只有两种可能为0,1,所以就把这两个串搞出来. 那么现在?取0和?取1不能并存,前缀不能并存,所以就是一个\(2-SAT\),现在问题在于这个东西可能会有很多条 ...

  9. LOJ #6036.「雅礼集训 2017 Day4」编码 Trie树上2-sat

    记得之前做过几道2-sat裸体,以及几道2-sat前缀优化建图,这道题使用了前缀树上前缀树优化建图.我们暴力建图肯定是n^2级别的,那么我们要是想让边数少点,就得使用一些骚操作.我们观察我们的限制条件 ...

随机推荐

  1. Android为TV端助力 apk静默安装

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/47803149 之前有很多朋友都问过我,在Android系统中怎样才能实现静默安装呢 ...

  2. python语言学习--1

    第一天 item: 当字符串中出现 反斜杠'\' 时,输出时会把它当做转义字符处理,所以结果中不会出现它,若要正常输出则需要在字符串前面加上r, 我想r的意思是religion即“原始”的意思: it ...

  3. (办公)重新选择一个开发工具Eclipse

    文章Eclipse内容摘抄自w3cschool的eclipse,原文地址:https://www.w3cschool.cn/eclipse/eclipse-run-configuration.html ...

  4. SQL Server如何查找表名或列名中包含空格的表和列

    最近发现一个数据库中的某个表有个字段名后面包含了一个空格,这个空格引起了一些小问题,一般出现这种情况,是因为创建对象时,使用双引号或双括号的时候,由于粗心或手误多了一个空格,如下简单案例所示: USE ...

  5. c/c++ 智能指针 shared_ptr 和 new结合使用

    智能指针 shared_ptr 和 new结合使用 用make_shared函数初始化shared_ptr是最推荐的,但有的时候还是需要用new关键字来初始化shared_ptr. 一,先来个表格,唠 ...

  6. c/ c++ 多态

    多态 1.多态用途 为了代码可以简单的重复使用,添加一个功能时,接口不需要修改. #include <iostream> using namespace std; class A{ pub ...

  7. visual Studio 中使用正则表达式来进行查找替换

    1.打开visual Studio 2. 通过菜单Edit -->Find and Replace -->Replace In File  ,或者使用  ctrl + Shift + H  ...

  8. FCM算法的matlab程序2

    FCM算法的matlab程序2 在“FCM算法的matlab程序”这篇文章中已经用matlab程序对iris数据库进行实现,并求解准确度.下面的程序是另一种方法,是最常用的方法:先初始化聚类中心,在进 ...

  9. Python语法基础-函数,类以及调试处理

    [TOC] 1. 函数的定义 python中函数有两种: python自带的函数 用户定义函数 返回多个值 原来返回值是一个tuple!但是,在语法上,返回一个tuple可以省略括号,而多个变量可以同 ...

  10. Linux之文档与目录结构

    Linux文件系统结构 Linux目录结构的组织形式和Windows有很大的不同.首先Linux没有“盘(C盘.D盘.E盘)”的概念.已经建立文件系统的硬盘分区被挂载到某一个目录下,用户通过操作目录来 ...