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) ...
随机推荐
- 关于R语言中dnorm,pnorm,qnorm,rnorm的用法
dnorm,pnorm,qnorm,rnorm的表达式: 其中x和q是由数值型变量构成的向量,p是由概率构成的向量,n是随机产生的个数 mean是要计算正态分布的均值,缺省值为0,sd是计算正态分布的 ...
- Python云端系统开发入门 pycharm代码
html <!DOCTYPE html><html><head> <meta charset="UTF-8"> <title& ...
- eclipse中不能保存汉字的解决方法
首先分清是打开jsp页面的问题还是java文件的问题? 对于java文件,只要在你的项目上点击右键选择“Propertise”(属性)然后点击“Info”标签将里面的Text file enco ...
- selenium模拟登陆淘宝
from selenium import webdriver import time from selenium.webdriver.common.by import By from selenium ...
- Linux sudoers
xxx is not in the sudoers file.This incident will be reported.的解决方法 - xiaochaoyxc - 博客园http://www.cn ...
- TCP 握手和挥手图解(有限状态机)
1.引言 TCP 这段看过好几遍,老是记不住,没办法找工作涉及到网络编程这块,各种问 TCP .今天好好整理一下握手和挥手过程.献给跟我一样忙碌,找工作的童鞋,欢迎大神批评指正. 2.TCP 的连接建 ...
- 6 基于Servlet 的分页
实现上图的分页功能,每页显示3条数据,每页显示3页供用户访问,点击左右双箭头时,可以跳转至上一个或者下一个大页,如点击右双箭头显示如下: 1 连接数据库的工具类 package com.latiny. ...
- Java内存泄漏分析
https://www.javatang.com/archives/2017/11/08/11582145.html?tdsourcetag=s_pcqq_aiomsg
- python之路--模块和包
一 . 模块 ⾸先,我们先看⼀个老⽣常谈的问题. 什么是模块. 模块就是⼀个包含了python定义和声明的⽂件, ⽂件名就是模块的名字加上.py后缀. 换句话说我们⽬前写的所有的py⽂件都可以看成是⼀ ...
- 如何抓取电商的数据 & Python
如何抓取电商的数据 & Python https://www.zhihu.com/question/40720286 https://www.zhihu.com/question/382455 ...