题意

题目链接:https://loj.ac/problem/6036

思路

​ 首先,有前缀关系的串不能同时存在,不难看出这是一个 2-sat 问题。先假设所有串都带问号,那么每一个字符串,我们可以把它的两种情况当成一个布尔值的 \(0/1\) 。然后互为前缀的串不能同时存在,最多连 \(n^2\) 条边,直接跑 2-sat 就在 \({\cal O}(n^2)\) 的时间内解决了本题。

​ 由于是个 \(01\) 字符串的前缀问题,我们不难想到字典树。要是能在字典树上体现前缀关系就好了。我们先退而求其次,先表达链式结构。

​ 我个人习惯用一个被划成两半的点来表示一个布尔值,左部分表示假,右部分表示真。就像这样:

​ 我们现在的问题是,对于 \(n\) 个布尔值,如何通过 \(\mathcal O(n)\) 建图表示这些布尔值最多只能存在一个真。化成 2-sat 的语言:它们两两的与运算都为 \(0\) 。

​ 这就是 2-sat 的前缀优化。再开一排新布尔值(用方形点来表示),采用下图方式连边:

​ 千言万语都不如一张图讲的清楚。你会发现,不管哪个原布尔值取了 \(1\) ,都可以通过方点告诉其他节点,“这条链里已经有人是真了,你们其他人都不能是真。”

​ 要特别注意的一点是,逆否边都是要连的,上图只是为了方便没有画出逆否边,具体写代码的时候也可以在连边函数中加上连逆否边防止忘连。

​ 有机化学都白学了吗?快告诉我上面的链中的链节是什么?

​ 不管有多长的链,都可以通过这种基团相连,处在同一链上的布尔值最多只有一个真。

​ 那么,树上的做法也就很显然了。

​ 上图就是一种树上的结构(例子举的不大好,请读者自行脑补一些树枝),它在字典树上看是这样的:

​ 上图的字典树中插入了 \(00,00,00,001\) 四个串,树上的一条向上的路径中不会同时有多个为真的布尔值。好写起见,我们索性在插入完一个串后的在字典树上可持久化出一个位置,插入我们的基团。这样写的话插入字符串要按长度升序来插。

代码

#include<bits/stdc++.h>
#define FOR(i, x, y) for(int i = (x), i##END = (y); i <= i##END; ++i)
#define DOR(i, x, y) for(int i = (x), i##END = (y); i >= i##END; --i)
template<typename T, typename _T> inline bool chk_min(T &x, const _T &y) {return y < x ? x = y, 1 : 0;}
template<typename T, typename _T> inline bool chk_max(T &x, const _T &y) {return x < y ? x = y, 1 : 0;}
typedef long long ll;
const int N = 500005 * 6;
const int M = 500005 * 16; template<const int N, const int M, typename T> struct Linked_List
{
int head[N], nxt[M], tot; T to[M];
Linked_List() {clear();}
T &operator [](const int x) {return to[x];}
void clear() {memset(head, -1, sizeof(head)), tot = 0;}
void add(int u, T v) {to[tot] = v, nxt[tot] = head[u], head[u] = tot++;}
#define EOR(i, G, u) for(int i = G.head[u]; ~i; i = G.nxt[i])
}; Linked_List<N, M, int> G; int rt, tot, ch[N][2];
int dfn[N], low[N], stk[N], bel[N], dfn_idx, scc, tp;
std::string str[N]; int ord[N];
int n, m; bool cmp(int a, int b) {return str[a].length() < str[b].length();} void tarjan(int u, int fa_e)
{
dfn[u] = low[u] = ++dfn_idx, stk[++tp] = u;
EOR(i, G, u)
{
if(i == (fa_e ^ 1)) continue;
int v = G[i];
if(!dfn[v])
{
tarjan(v, i);
chk_min(low[u], low[v]);
}
else if(!bel[v] && dfn[v] < dfn[u])
chk_min(low[u], dfn[v]);
}
if(dfn[u] == low[u])
{
scc++;
do bel[stk[tp]] = scc; while(stk[tp--] != u);
}
} void link(int u, int v)
{
G.add(u, v), G.add(v ^ 1, u ^ 1);
} void insert(std::string &str, int t)
{
if(!rt) rt = ++tot;
int k = rt, las;
FOR(i, 0, (int)str.length() - 1)
{
if(!ch[k][str[i] - '0'])
{
ch[k][str[i] - '0'] = ++tot;
link(k << 1 | 1, ch[k][str[i] - '0'] << 1 | 1);
}
las = k, k = ch[k][str[i] - '0'];
}
tot++;
link(k << 1 | 1, tot << 1 | 1);
link(t, tot << 1 | 1);
link(k << 1 | 1, t ^ 1);
ch[las][str[(int)str.length() - 1] - '0'] = tot;
} int main()
{
scanf("%d", &n);
rt = 0, tot = n;
FOR(i, 1, n) std::cin >> str[i], ord[i] = i;
std::sort(ord + 1, ord + 1 + n, cmp);
FOR(i, 1, n)
{
bool flg = 0;
FOR(j, 0, (int)str[ord[i]].length() - 1) if(str[ord[i]][j] == '?')
{
str[ord[i]][j] = '0', insert(str[ord[i]], i << 1);
str[ord[i]][j] = '1', insert(str[ord[i]], i << 1 | 1);
flg = 1;
break;
}
if(!flg)
{
insert(str[ord[i]], i << 1);
link(i << 1 | 1, i << 1);
}
}
FOR(i, 2, tot << 1 | 1) if(!dfn[i]) tarjan(i, -1);
FOR(i, 1, tot) if(bel[i << 1] == bel[i << 1 | 1])
{
puts("NO");
return 0;
}
puts("YES");
return 0;
}

雅礼集训 2017 Day4 编码(2-sat)的更多相关文章

  1. Loj 6036 「雅礼集训 2017 Day4」编码 - 2-sat

    题目传送门 唯一的传送门 题目大意 给定$n$个串,每个串只包含 ' .问是否可能任意两个不同的串不满足一个是另一个的前缀. 2-sat的是显然的. 枚举每个通配符填0还是1,然后插入Trie树. 对 ...

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

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

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

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

  4. loj 6037 「雅礼集训 2017 Day4」猜数列 - 动态规划

    题目传送门 传送门 题目大意 有一个位置数列,给定$n$条线索,每条线索从某一个位置开始,一直向左或者向右走,每遇到一个还没有在线索中出现的数就将它加入线索,问最小的可能的数列长度. 依次从左到右考虑 ...

  5. 2018.10.27 loj#6035. 「雅礼集训 2017 Day4」洗衣服(贪心+堆)

    传送门 显然的贪心题啊...考试没调出来10pts滚了妙的一啊 直接分别用堆贪心出洗完第iii件衣服需要的最少时间和晾完第iii件衣服需要的最少时间. 我们设第一个算出来的数组是aaa,第二个是bbb ...

  6. LOJ#6035. 「雅礼集训 2017 Day4」洗衣服

    传送门 先处理出每一件衣服最早什么时候洗完,堆+贪心即可 然后同样处理出每件衣服最早什么时候烘干 然后倒序相加取最大值 # include <bits/stdc++.h> using na ...

  7. LOJ #6037.「雅礼集训 2017 Day4」猜数列 状压dp

    这个题的搜索可以打到48分…… #include <cstdio> #include <cstring> #include <algorithm> ; bool m ...

  8. LOJ #6035.「雅礼集训 2017 Day4」洗衣服 贪心

    这道题的贪心好迷啊~我们对于两个过程进行单独贪心,然后再翻转一个,把这两个拼起来.先说一下单独贪心,单独贪心的话就是用一个堆,每次取出最小的,并且把这个最小的加上他单次的,再放进去.这样,我们得到的结 ...

  9. 「6月雅礼集训 2017 Day4」qyh(bzoj2687 交与并)

    原题传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2687 [题目大意] 给出若干区间,求一个区间的大于等于2的子集,使得 |区间并| 和 | ...

随机推荐

  1. Vue.js 源码分析(三) 基础篇 模板渲染 el、emplate、render属性详解

    Vue有三个属性和模板有关,官网上是这样解释的: el ;提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标 template ;一个字符串模板作为 Vue 实例的标识使用.模板将会 ...

  2. 统一批处理流处理——Flink批流一体实现原理

    实现批处理的技术许许多多,从各种关系型数据库的sql处理,到大数据领域的MapReduce,Hive,Spark等等.这些都是处理有限数据流的经典方式.而Flink专注的是无限流处理,那么他是怎么做到 ...

  3. kali渗透综合靶机(十五)--Breach-1.0靶机

    kali渗透综合靶机(十五)--Breach-1.0靶机 靶机下载地址:https://download.vulnhub.com/breach/Breach-1.0.zip 一.主机发现 1.netd ...

  4. Asp.Net MVC 的19个管道事件

    httpApplication调用ProcessRequest方法,内部执行19个管道事件,如下 BeginRequest  开始处理请求 AuthenticateRequest 授权验证请求开始,获 ...

  5. Google开发者F12工具面板-network详解

    1 开发者工具面板    面板上包含了Elements面板.Console面板.Sources面板.Network面板.Performance面板.Memory面板.Application面板.Sec ...

  6. badboy录制过程中出现当前页面的脚本发现错误

    为什么出现这个提示 , 是因为访问者使用的浏览器不能完全支持页面里的脚本,毕竟版本太老,一直没有更新 ,这个版本错误并不会影响使用,有强迫症的可以关闭下,

  7. JMeter之Http协议接口性能测试--基础

    一.不同角色眼中的接口 1.1,开发人员眼中的接口    1.2,测试人员眼中的接口 二.Http协议基本介绍 2.1,常见的接口协议 1.:2. :3. :4.:5.: 6. 2.2,Http协议栈 ...

  8. rem与em的使用和区别详解【转】

    目录 最大的问题是 主要区别  rem 单位如何转换为像素值  em 单位如何转换为像素值 Em 单位的遗传效果 Em 继承的例子 浏览器设置 HTML 元素字体大小的影响 没有设置 HTML 字体大 ...

  9. select子句

    1.order by order by 字段1 升序或者降序,字段2 升序或者降序(dsc) 默认 升序(asc) 注意:如果是分组,则应该使用对分组字段进行排序的groupby语法 group by ...

  10. [小程序]微信小程序获取input并发送网络请求

    1. 获取输入框数据wxml中的input上增加bindinput属性,和方法值在js部分定义与之对应的方法,只要在输入的时候,数据就会绑定调用到该方法,存入data属性变量中 2. 调用get请求发 ...