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) ...
随机推荐
- 牛客练习赛38 D 题 出题人的手环 (离散化+树状数组求逆序对+前缀和)
链接:https://ac.nowcoder.com/acm/contest/358/D来源:牛客网 出题人的手环 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 524288K,其他 ...
- Column 'parent_id' specified twice
Hibernate Column 'parent_id' specified twice问题解决--insertable = false, updatable = false的使用 - shendeg ...
- Pseudo Registers
Pseudoregister Description @ERR Last error value; the same value returned by the GetLastError() API ...
- C++多态(静多态和动多态)
如今的C++已经是个多重泛型编程语言(multiparadigm programming lauguage),一个同时支持过程形式(procedural).面向对象形式(object-oriented ...
- Appscanner实验还原code3
# Author: Baozi #-*- codeing:utf-8 -*- import _pickle as pickle from sklearn import ensemble import ...
- java 调用 wsdl形式的webservice 示例
import java.rmi.RemoteException; import javax.xml.rpc.ParameterMode; import javax.xml.rpc.ServiceExc ...
- VS Code & MacOS & Zsh
VS Code & MacOS & Zsh https://code.visualstudio.com/docs/setup/mac solution OK bug $ cat .zs ...
- AI算法第一天【概述与数学初步】
1. 机器学习的定义: 机器从数据中学习出规律和模式,以应用在新数据上作出预测的任务 2.学习现象: (1)语言文字的认知识别 (2)图像,场景,物体的认知和识别 (3)规则:下雨天要带雨伞 (4)复 ...
- Fiddler-学习笔记-远程抓包
1 操作系统低于win7用 fiddler 2 win7 或win7以上版本,用 fiddler4片本 2 fiddler开关:左下角或点击F12控件fiddler开关,开=capturing 3 启 ...
- fiddler 笔记-重定向
重定向功能:主要是进行会话的拦截,然后替换原始资源的功能 选择请求-到autoresponser面板-勾选 enable rules :add rules 设置如下: 2 在浏览器中请示url-页面跳 ...