题意

给你 \(n\) 个 \(01\) 串 \(S\) ,其中有些位置可能为 \(?\) 表示能任意填 \(0/1\) 。问对于所有填法,把所有串插入到 \(Trie\) 的节点数之和(空串看做根节点)。

\(n \le 20, 1 \le |S_i| \le 50\)

题解

直接算显然不太好算的。

\(Trie\) 的节点数其实就是本质不同的前缀个数,可以看做 \(n\) 个串的所有前缀的并集的大小。

我们可以套用容斥原理最初的式子。

\[\left| \bigcup_{i=1}^n A_i \right| = \sum_{k = 1}^{n} (-1)^{k - 1} \sum_{1 \le i_1 < i_2 < \cdots < i_k \le n} |A_{i_1} \cap A_{i_2} \cap \cdots \cap A_{i_k}|
\]

这样的话,我们就可以转化成对于每个子集的交集了,也就是公共前缀的个数。

我们设 \(f(S)\) 为 \(S\) 集合内的所有子串对于 所有填的方案 的公共前缀的个数。

那么答案为 \(ans = \sum_{S \subseteq T} (-1)^{|S| - 1} f(S)\)

如何得到呢?由于 \(n\) 很小我们可以暴力枚举集合,然后枚举当前前缀的长度,直接计数。

  1. 如果当前所有的都是 \(?\) 那么意味着可以任意填 \(0/1\) 。
  2. 如果存在一种数字,其他都是 \(?\) ,那么意味着只能填这种数字。
  3. 如果存在两种数字,那么之后都不可能为公共前缀了,直接退出即可。

直接实现是 \(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(容斥原理)的更多相关文章

  1. HDU 5831 Rikka with Parenthesis II(六花与括号II)

    31 Rikka with Parenthesis II (六花与括号II) Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536 ...

  2. HDU 5831 Rikka with Parenthesis II (栈+模拟)

    Rikka with Parenthesis II 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5831 Description As we kno ...

  3. 【Hihocoder1413】Rikka with String(后缀自动机)

    [Hihocoder1413]Rikka with String(后缀自动机) 题面 Hihocoder 给定一个小写字母串,回答分别把每个位置上的字符替换为'#'后的本质不同的子串数. 题解 首先横 ...

  4. hdu 5831 Rikka with Parenthesis II 线段树

    Rikka with Parenthesis II 题目连接: http://acm.hdu.edu.cn/showproblem.php?pid=5831 Description As we kno ...

  5. HDU 5831 Rikka with Parenthesis II (贪心)

    Rikka with Parenthesis II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  6. hdu 5831 Rikka with Parenthesis II 括号匹配+交换

    Rikka with Parenthesis II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Jav ...

  7. hdu.5202.Rikka with string(贪心)

    Rikka with string Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others ...

  8. [LeetCode] 344 Reverse String && 541 Reverse String II

    原题地址: 344 Reverse String: https://leetcode.com/problems/reverse-string/description/ 541 Reverse Stri ...

  9. leetcode 344. Reverse String 、541. Reverse String II 、796. Rotate String

    344. Reverse String 最基础的旋转字符串 class Solution { public: void reverseString(vector<char>& s) ...

随机推荐

  1. 反射reflect

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意方法和属性:这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制. ...

  2. 06_Hadoop分布式文件系统HDFS架构讲解

    mr  计算框架 假如有三台机器 统领者master 01  02  03  每台机器都有过滤的应用程序 移动数据 01机== 300M  >mr 移动计算  java程序传递给各个机器(mr) ...

  3. C#设计模式之1:策略模式

    首先需要说明的是该系列的所有内容都是基于headfirst设计模式来描述的.因为我之前也看过不少关于设计模式的书,还是发现这本最好,因为这本书里面给出的例子是最贴切实际的.不说了,开始这个系列吧! 策 ...

  4. composer更改源为国际

    composer config -g repo.packagist composer https://repo.packagist.org

  5. [转帖]Windows 内核说明

    来源:https://zhidao.baidu.com/question/398191459.html 自己的理解. windows 的内核文件 是在 c:\windows\system32 目录下面 ...

  6. spring mvc常用注解总结

    1.@RequestMapping@RequestMappingRequestMapping是一个用来处理请求地址映射的注解(将请求映射到对应的控制器方法中),可用于类或方法上.用于类上,表示类中的所 ...

  7. 临时的ThisCall

    // 获取当前定位 changeCity: function () { let that = this; that.locationClose(); Upj._changeCity().then((d ...

  8. flutter开发vscode用模拟器调试

    android studio的太重,我装的是android sdk,使用avd的模拟器启动黑屏 启动夜神模拟器(已卸载) 建立连接: adb connect 127.0.0.1:62001    (夜 ...

  9. Field tTypeMapper in com.atguigu.project.service.imp.projectInfoServiceImpl required a bean of type 'com.atguigu.project.mapper.TTypeMapper' that could not be found.

    解决:MapperScan

  10. Python cmd库的简易使用

    简单记录一下,竟然这么简单的方法就能在 python 里面实现一个简单的交互式命令行以前从来没有尝试过. 上一个完整的例子: import cmd import osimport readline r ...