BZOJ 4762: 最小集合
来源:互联网 发布:妙味云课堂js视频下载 编辑:程序博客网 时间:2024/04/27 10:33
Description
定义一个非空集合是合法的,当且仅当它满足以下两个条件。
1、集合内所有元素and和为0
2、它的非空子集中仅有它本身满足1
给出一个集合S,求它的合法非空子集数。
Input
第一行一个正整数n,表示|S|
第二行n个非负整数ai,表示集合内的元素。
n≤1000,ai<1024
Output
一个整数,表示S的合法非空子集数。答案可能很大,请mod 1e9+7之后输出。
Sample Input
4
1 2 4 4
Sample Output
5
样例解释:满足条件的集合为{1,2},{1,4},{1,4},{2,4},{2,4}
分析
转自栋爷的博客
先把给定集合所有数取反。
比如有效位数是4位,1101就变成0010。
那么问题变成,所有元素or和为1023,而去掉任意一个元素后or和均不为1023。
那么接下来我们来设一个诡异的状态。
因为要知道去掉一个人元素会不会使or和为1023,因此我们前后都要知道。
可以设一个f[i,j,k]表示做完了i个数,前面选择的一些数or和为j,我们希望后面选出的数or和为k。
然而随便推一推都觉得不会转移啊。。
这时赶紧改一下状态,设f[i,j,k]表示做完了i个数,前面选择的一些数or和为j,我们希望后面选出的数or和包含k(什么叫包含?x包含k需满足x&k=k)
设第i+1个数为x。
不选?
f[i+1][j][k]+=f[i][j][k]
选呢?
假设后面部分不包含x时是k’。
我们发现k’需要满足两个条件:
1、k’|x=k(根据状态定义)
2、k’|j|x!=k’|j(如果等了那么就能去掉x了)
满足条件的k’肯定存在一些包含关系,而我么的状态设的也是包含,所以转移会比较方便。
先只考虑满足第一个条件:
f[i+1][j|x][k^(k&x)]+=f[i][j][k]
这个很容易考虑,如果k的某一位有1,x该位也有1,那么k’的这一位可以是0也可以为1。
再去掉满足第一个条件而不满足第二个条件的:
f[i+1][j|x][(k^(k&x))|(x^(x&j))]-=f[i][j][k]
这是个什么意思?先看后面,显然只有x的某一位是1,而j的对应位是0时才有1的贡献,意思就是x会给j的哪些原本没有1的位变成1。
而如果k’的这些位也有1,那么j|k’后,再或x将不变,因此会不满足第二个条件。
然后这个dp就是正确的,初始f[0][0][0]=1,最后答案是f[n][1023][0]。
假设有m位,这样做是n∗4^m
考虑优化吧。我们来证明,如果f[i][j][k]不为0,一定有j包含k。
初始时显然满足。
看第一条转移,f[i+1][j|x][k^(k&x)]+=f[i][j][k]。
假如j包含k,k的某位为0时,k&x的对应位肯定也是0,所以k^(k&x)并不会多1,反而可能少1,但j|x不会少1,因此有j|x包含k^(k&x)。
看第二条转移,f[i+1][j|x][(k^(k&x))|(x^(x&j))]-=f[i][j][k]。
假如j包含k,记k’=k^(k&x)。假如k’某位为1,可以不管它,由上面的结论j|x的这位也会是1。假如k某位为0,k’这位也是0,而x^(x&j)是1,那么最终这位会是1,而因为x^(x&j)的这位是1,x这位必须是1,那么j|x这位也有1了。
这样枚举j后每次只需枚举一个j包含的k,有个快捷的枚举方法见代码。
那么可以证明复杂度降为
n∗3^m
代码
#include <bits/stdc++.h>#define N 1030#define MOD 1000000007using namespace std;int n;int f[2][N][N], tmp;void ADD(int &t, int d){ t += d; if (t >= MOD) t -= MOD;}int main(){ scanf("%d", &n); f[0][1023][1023] = 1; for (int i = 1; i <= n; ++ i) { int d; scanf("%d", &d); tmp ^= 1; for (int j = 0; j < 1024; ++ j) { for (int k = j; k; k = (k - 1) & j) f[tmp][k][j] = 0; f[tmp][0][j] = 0; } for (int j = 0; j < 1024; ++ j) { for (int k = j; k; k = (k - 1) & j) if (f[!tmp][k][j]) { ADD(f[tmp][k][j], f[!tmp][k][j]); ADD(f[tmp][k & d][j & d], f[!tmp][k][j]); ADD(f[tmp][k & d][j & (k | d)], MOD - f[!tmp][k][j]); } ADD(f[tmp][0][j], f[!tmp][0][j]); ADD(f[tmp][0][j & d], f[!tmp][0][j]); ADD(f[tmp][0][j & (0 | d)], MOD - f[!tmp][0][j]); } } printf("%d", f[tmp][0][0]);}
- BZOJ 4762: 最小集合
- [容斥] BZOJ 4762 最小集合
- [DP][容斥原理] BZOJ 4762: 最小集合
- 最小集合
- BZOJ 1486 最小圈
- bzoj 2132(最小割)
- bzoj 1934(最小割)
- bzoj 3275(最小割)
- bzoj 3396(最小割)
- bzoj 2561(最小割)
- bzoj 3894(最小割)
- BZOJ 1497 最小割
- BZOJ 2039 最小割
- BZOJ 1412 最小割
- bzoj 1001 最小割
- bzoj 2561 最小割
- BZOJ 1787 紧急集合
- bzoj 2839 集合计数
- 数据结构-顺序表与单链表的C++模板类实现
- uC/OSIII学习笔记(一)消息传递
- 文件存储简单代码
- 前端面试(计算机网络、数据算法、Linux)
- Count Primes
- BZOJ 4762: 最小集合
- LINK : fatal error LNK1104: 无法打开文件“opencv_calib3d240d.lib”解决方法
- EM算法--二维高斯混合模型(GMM)
- 字符判断| 对于汉字的输入确实有问题,求指导
- Android Manifest之<application>元素中文注释
- makefile教程
- Unity3D 地形整体高度升降 插件
- PHP 使用 PDO 的 execute () 方法 删除不存在的数据,返回值仍然是 true 是什么鬼
- 初识庐山真面目-MySQL数据库