题目传送门

  唯一的传送门

题目大意

  给定$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. 11.15luffycity(7)

    2018-11-15 17:43:50 还有一点路飞项目就结束啦! 周日打算回去! 双十一的耳机到啦,音质确实不错!2333! 等着项目做完,完整总结一下! 越努力,越幸运!永远不要高估自己!!!! ...

  2. git 处理

    [添加]cd 路径(进入文件夹) git clone url git status (查看状态) cd firstprojected 进入这个文件夹 查看 git status 有 index.php ...

  3. java实现爬虫功能

    /** * 爬取新闻信息,封装成实体bean */public class GetNews { public List<News> getNews() {  // 存储新闻对象  List ...

  4. ffmpeg 转换 mp4 成 flv

    参考资料: https://addpipe.com/blog/flv-to-mp4/ ffmpeg -i demo.mp4 -c:v libx264 -crf 19 demo.flv 或者 ffmpe ...

  5. aes 和 Md5 分析

    高级加密标准(英语:Advanced Encryption Standard,缩写:AES). 密码的设计力求满足以下3条标准: ① 抵抗所有已知的攻击. ② 在多个平台上速度快,编码紧凑. ③ 设计 ...

  6. 通过ICE轻松部署WES7镜像

    作者:雷志刚 转自:http://lzg-ad.blog.sohu.com/156323256.html 注:该文转自Happymy,感谢他的技术提供和分享. 本文适合的软件版本:CTP,RC 如果大 ...

  7. 客户续费模型 逻辑回归 分类器 AdaBoost

    客户续费模型  逻辑回归 分类器  AdaBoost

  8. npm 镜像的问题

    1> cnpm(不推荐) npm install -g cnpm --registry=https://registry.npm.taobao.org 2> 推荐第二种 npm confi ...

  9. 18.1-uC/OS-III等待多个内核对象

    等待的多个内核对象是指多值信号量和消息队列的任意组合 . 如果想要使用“等待多个内核对象”,就必须事先使能“等待多个内核对象”.“等待多个内核对象” 的使能位于“os_cfg.h”. 1.OSPend ...

  10. 关于systemctl

    systemctl是CentOS7的服务管理工具中主要的工具,它融合之前service和chkconfig的功能于一体. 启动一个服务:systemctl start firewalld.servic ...