题目传送门

  唯一的传送门

题目大意

  给定$n$个串,每个串只包含 '','','?' ,其中 '?' 至多在每个串中出现1次,它可以被替换为 '' 或 '' 。问是否可能任意两个不同的串不满足一个是另一个的前缀。

  2-sat的是显然的。

  枚举每个通配符填0还是1,然后插入Trie树。

  对于Trie的每个点在2-sat中建点。

  如果其中一个点被选择,那么它祖先和所有后继的结束点都不能选。(然后逆否命题连边)

  对于一个包含通配符的串,通配符替换为0以及通配符替换为1的否命题等价,同样,通配符替换为1以及通配符替换为0的否命题等价(连双向边)。

  对于一个不包含通配符的串,直接它到的节点的从否命题节点连一条到它的有向边。

  于是我们得到边数平方的优秀做法。

  考虑前缀和优化,新建一个虚点表示Trie树上,它的祖先的命题都是假。但是这样还有点问题,还要建一个点表示Trie上,它的所有后继的命题都是假。

  建图比较繁杂,见下图:

  然后再说一下处理多个串在Trie树上共点。

  于是便有了点数$12n$的优秀做法。再由于代码常数巨大无比,成功Loj最慢榜榜首,sad。。。(一定要去学习一下榜首同学点数$4n$的优质做法)

Code

 /**
* loj.ac
* Problem#6036
* Accepted
* Time: 4346ms
* Memory: 469852k
*/
#include <bits/stdc++.h>
using namespace std;
typedef bool boolean; const int N = 5e5 + ; typedef class TrieNode {
public:
int id;
TrieNode* ch[];
vector<int> vs;
}TrieNode; TrieNode pool[N << ];
TrieNode* top = pool; TrieNode* newnode() {
top->id = ;
top->ch[] = top->ch[] = NULL;
return top++;
} typedef class Trie {
public:
TrieNode* rt; Trie():rt(newnode()) { } int insert(char* str, int id) {
TrieNode* p = rt;
for (int i = , c; str[i]; i++) {
c = str[i] - '';
if (!p->ch[c])
p->ch[c] = newnode();
p = p->ch[c];
}
if (!p->id)
p->id = id;
else if ((!p->vs.size() || p->vs[p->vs.size() - ] != id - ) && p->id != id - )
p->vs.push_back(id);
return p->id;
}
}Trie; int n, n2;
char buf1[N], buf2[N];
vector<int> g[N * ];
// 1 ~ 2 * n2 : Is the node seclected?
// 2 * n2 + 1 ~ 4 * n2: Aren't nodes above seclected?
// 4 * n2 + 1 ~ 6 * n2: Aren't nodes below seclected?
Trie tr; inline void init() {
scanf("%d", &n);
n2 = n << ;
for (int i = , len, p1, p2; i <= n; i++) {
scanf("%s", buf1);
len = strlen(buf1);
memcpy(buf2, buf1, len + );
for (int j = ; j < len; j++)
if (buf1[j] == '?')
buf1[j] = '';
for (int j = ; j < len; j++)
if (buf2[j] == '?')
buf2[j] = '';
p1 = tr.insert(buf1, (i << ) - );
p2 = tr.insert(buf2, i << );
if (p1 == p2) {
g[(i << ) - + n2].push_back((i << ) - );
} else {
p1 = ((i << ) - ), p2 = (i << );
g[p1].push_back(p2 + n2);
g[p2 + n2].push_back(p1);
g[p2].push_back(p1 + n2);
g[p1 + n2].push_back(p2);
}
}
} #define virt(_x) (_x + n2)
#define upre(_x) (_x + n2 * 2)
#define upvi(_x) (_x + n2 * 3)
#define dore(_x) (_x + n2 * 4)
#define dovi(_x) (_x + n2 * 5) void dfs(TrieNode* p, int last) {
if (!p) return;
if (p->vs.size()) {
vector<int> &ve = p->vs;
for (int i = ; i < (signed) ve.size(); i++) {
TrieNode* q = newnode();
q->id = ve[i];
q->ch[] = p->ch[], q->ch[] = p->ch[];
p->ch[] = NULL, p->ch[] = q;
}
ve.clear();
}
int id = p->id;
if (id) {
g[id].push_back(upvi(id));
g[id].push_back(dovi(id));
g[upre(id)].push_back(virt(id));
g[dore(id)].push_back(virt(id));
if (last) {
g[id].push_back(upre(last));
g[upvi(last)].push_back(virt(id));
g[last].push_back(dore(id));
g[dovi(id)].push_back(virt(last)); g[upre(id)].push_back(upre(last));
g[upvi(last)].push_back(upvi(id));
g[dore(last)].push_back(dore(id));
g[dovi(id)].push_back(dovi(last));
}
}
int nid = ((id) ? (id) : (last));
dfs(p->ch[], nid);
dfs(p->ch[], nid);
} int dfs_clock = ;
stack<int> st;
boolean vis[N * ], ins[N * ];
int dfn[N * ], low[N * ];
void tarjan(int p) {
vis[p] = true, ins[p] = true;
dfn[p] = low[p] = ++dfs_clock;
st.push(p);
for (int i = ; i < (signed) g[p].size(); i++) {
int e = g[p][i];
if (!vis[e]) {
tarjan(e);
low[p] = min(low[p], low[e]);
} else if (ins[e])
low[p] = min(low[p], dfn[e]);
} if (dfn[p] == low[p]) {
int cur;
do {
cur = st.top();
st.pop();
low[cur] = low[p];
ins[cur] = false;
} while (cur != p);
}
} void putans(const char* str) {
puts(str);
exit();
} inline void solve() {
dfs(tr.rt, );
for (int i = ; i <= n2 * ; i++)
if (!vis[i])
tarjan(i);
for (int i = ; i <= n2; i++) {
if (low[i] == low[virt(i)])
putans("NO");
if (low[upre(i)] == low[upvi(i)])
putans("NO");
if (low[dore(i)] == low[dovi(i)])
putans("NO");
}
putans("YES");
} int main() {
init();
solve();
return ;
}

Loj 6036 「雅礼集训 2017 Day4」编码 - 2-sat的更多相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  8. [LOJ 6031]「雅礼集训 2017 Day1」字符串

    [LOJ 6031] 「雅礼集训 2017 Day1」字符串 题意 给定一个长度为 \(n\) 的字符串 \(s\), \(m\) 对 \((l_i,r_i)\), 回答 \(q\) 个询问. 每个询 ...

  9. [LOJ 6030]「雅礼集训 2017 Day1」矩阵

    [LOJ 6030] 「雅礼集训 2017 Day1」矩阵 题意 给定一个 \(n\times n\) 的 01 矩阵, 每次操作可以将一行转置后赋值给某一列, 问最少几次操作能让矩阵全为 1. 无解 ...

随机推荐

  1. Spring AOP 自动创建代理

        Spring为我们提供了自动代理机制,让容器为我们自动生成代理,把我们从烦琐的配置工作中解放出来,在内部,Spring 使用BeanPostProcessor自动地完成这项工作.   1.实现 ...

  2. Mysql命令行tab自动补全方法

    在mysql命令行有时为了方便想要按tbl键自动补全命令,以便节约时间. 具体方法如下: 第一步:修改my.cnf vi mysql/etc/my.cnf 将下图红框的代码注释,修改成如下代码: #d ...

  3. 使用nginx运行thinkphp的nginx配置

    location / { index index.php; #如果文件不存在则尝试TP解析 if (!-e $request_filename) { rewrite ^(.*)$ /index.php ...

  4. npm安装插件

    参考:https://www.cnblogs.com/ayseeing/p/3624058.html

  5. GetLastError获取到错误代码的含义

    在写win32的时候我们会用到GetLastError()函数来获取程序错误信息,那我们如何从返回的数字得到错误信息. 这里推荐一个博客,总结了所以返回数字的错误信息: http://blog.csd ...

  6. antd-mobile Picker组件默认值

    import { createForm } from "rc-form"; @createForm() class TopAdSlots extends Component { @ ...

  7. mysql拿webshell总结

    1.select '<?php eval($_POST[jumbo]) ?>' into outfile '/var/www/jumbo.php'; 2.select '<?php ...

  8. python语法_使用占位符进行格式化输出

    “%s”   占位符 name = input("name:") age = input("age:") job = input("job:" ...

  9. 20175320 2018-2019-2 《Java程序设计》第1周学习总结

    20175320 2018-2019-2 <Java程序设计>第1周学习总结 教材学习内容总结 本周学习了教材的第一章内容.在这一章中,我了解了Java这门编程语言的特点和地位,而本章的重 ...

  10. day5_不能循环删除list-深拷贝、浅拷贝(import copy)

    一.循环删list里面的元素,会导致下标错位,结果是不对的举例:想删除奇数 l = [1,1,1,2,3,4,5] for i in l: if i%2 !=0: l.remove(i) #删除后,导 ...