hihoCoder #1646 : Rikka with String II(容斥原理)
题意
给你 \(n\) 个 \(01\) 串 \(S\) ,其中有些位置可能为 \(?\) 表示能任意填 \(0/1\) 。问对于所有填法,把所有串插入到 \(Trie\) 的节点数之和(空串看做根节点)。
\(n \le 20, 1 \le |S_i| \le 50\)
题解
直接算显然不太好算的。
\(Trie\) 的节点数其实就是本质不同的前缀个数,可以看做 \(n\) 个串的所有前缀的并集的大小。
我们可以套用容斥原理最初的式子。
\]
这样的话,我们就可以转化成对于每个子集的交集了,也就是公共前缀的个数。
我们设 \(f(S)\) 为 \(S\) 集合内的所有子串对于 所有填的方案 的公共前缀的个数。
那么答案为 \(ans = \sum_{S \subseteq T} (-1)^{|S| - 1} f(S)\)
如何得到呢?由于 \(n\) 很小我们可以暴力枚举集合,然后枚举当前前缀的长度,直接计数。
- 如果当前所有的都是 \(?\) 那么意味着可以任意填 \(0/1\) 。
- 如果存在一种数字,其他都是 \(?\) ,那么意味着只能填这种数字。
- 如果存在两种数字,那么之后都不可能为公共前缀了,直接退出即可。
直接实现是 \(O(2^n n |S|)\) 的。可以把状态集合合并一下优化到 \(O(2^n |S|)\) 。(但是我太懒了)
代码
实现的时候不要忘记是所有填的方案。
#include <bits/stdc++.h>
#define For(i, l, r) for (register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for (register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Rep(i, r) for (register int i = (0), i##end = (int)(r); i < i##end; ++i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
using namespace std;
template<typename T> inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }
template<typename T> inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }
inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
}
void File() {
#ifdef zjp_shadow
freopen ("1646.in", "r", stdin);
freopen ("1646.out", "w", stdout);
#endif
}
const int N = 21, L = 51, Mod = 998244353;
int n, len[1 << N], Pow[N * L];
char str[N][L];
int main () {
File();
n = read();
Set(len, 0x3f);
int tot = 0;
Rep (i, n) {
scanf ("%s", str[i] + 1);
len[1 << i] = strlen(str[i] + 1);
For (j, 1, strlen(str[i] + 1))
if (str[i][j] == '?') ++ tot;
}
Pow[0] = 1;
For (i, 1, tot)
Pow[i] = 2ll * Pow[i - 1] % Mod;
Rep (i, 1 << n)
chkmin(len[i], min(len[i ^ (i & -i)], len[i & -i]));
int ans = 0;
Rep (i, 1 << n) if (i) {
int res = 0, sum = tot, pre = 0;
For (j, 1, len[i]) {
int flag = 0, now = 0;
Rep (k, n) if (i >> k & 1) {
if (str[k][j] == '?') ++ now;
else flag |= (str[k][j] - '0' + 1);
}
sum -= now;
if (flag == 3) break;
if (!flag) ++ pre;
res = (res + Pow[pre + sum]) % Mod;
}
ans = (ans + (__builtin_popcount(i) & 1 ? 1 : -1) * res) % Mod;
}
ans += Pow[tot]; if (ans < 0) ans += Mod;
printf ("%d\n", ans);
return 0;
}
hihoCoder #1646 : Rikka with String II(容斥原理)的更多相关文章
- HDU 5831 Rikka with Parenthesis II(六花与括号II)
31 Rikka with Parenthesis II (六花与括号II) Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536 ...
- HDU 5831 Rikka with Parenthesis II (栈+模拟)
Rikka with Parenthesis II 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5831 Description As we kno ...
- 【Hihocoder1413】Rikka with String(后缀自动机)
[Hihocoder1413]Rikka with String(后缀自动机) 题面 Hihocoder 给定一个小写字母串,回答分别把每个位置上的字符替换为'#'后的本质不同的子串数. 题解 首先横 ...
- hdu 5831 Rikka with Parenthesis II 线段树
Rikka with Parenthesis II 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5831 Description As we kno ...
- HDU 5831 Rikka with Parenthesis II (贪心)
Rikka with Parenthesis II Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- hdu 5831 Rikka with Parenthesis II 括号匹配+交换
Rikka with Parenthesis II Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Jav ...
- hdu.5202.Rikka with string(贪心)
Rikka with string Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others ...
- [LeetCode] 344 Reverse String && 541 Reverse String II
原题地址: 344 Reverse String: https://leetcode.com/problems/reverse-string/description/ 541 Reverse Stri ...
- leetcode 344. Reverse String 、541. Reverse String II 、796. Rotate String
344. Reverse String 最基础的旋转字符串 class Solution { public: void reverseString(vector<char>& s) ...
随机推荐
- JS闭包以及作用域初探
以前看到的一个问题,很有意思: for (var i = 0; i < 5; i++) { setTimeout(function () { console.log(i); },500); } ...
- TCP 原理
一.分组交换网络 古老的电话通信,一根电缆,两个用户设备通信 计算机中的两个设备节点通信:分组网络 计算机网络采取分组交换技术,意思就是我有[一块数据]要发给对方,那我会把这[一块数据]分成N份[ ...
- 《梦断代码》Scott Rosenberg著(一)
两打程序员,3年时间,4732个bug,只为打造超卓软件. --序 在我们平时看到的大部分书籍只是讲技术和理论,但我们其实并不知道在真实的软件开发过程中,这些技术和理论究竟是被什么样的人如何去使用, ...
- Linux sed使用方法
目录 sed处理流程 测试数据 sed命令格式 sed命令行格式 行定位 定位1行 定位区间行(多行) 定位某一行之外的行 定位有跨度的行 操作命令 -a (新增行) -i(插入行) -c(替代行) ...
- 【kindle笔记】之 《明朝那些事儿》-2018-7-1
[kindle笔记]读书记录-总 最近在读这本书.之前在微信读书里断断续续读过,读到深处还想蹦起来做笔记那种.后来种种原因断了,再没续上. 现在又开始啦.最近还在重八兄造反阶段,还很早呢,有时候晚上玩 ...
- 爬虫——scrapy框架
Scrapy是一个异步处理框架,是纯Python实现的爬虫框架,其架构清晰,模块之间的耦合程度低,可拓展性强,可以灵活完成各种需求.我们只需要定制几个模块就可以轻松实现一个爬虫. 1.架构 Scra ...
- Centos下启动和关闭MySQL
https://blog.csdn.net/gghh2015/article/details/78281585
- Python + selenium + pycharm 环境部署细节 和selenium、Jenkins简单介绍
一.测试体系:Python + selenium + pycharm + Jenkins/docker 环境搭建: 1.安装python 3.4/3.5 2/3.6/ 3.7 2.配置环境变量 3.p ...
- [转帖]buffer与cache的区别
作者:沈万马链接:https://www.zhihu.com/question/26190832/answer/146259979来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...
- 【学亮IT手记】利用字节流复制文件