[转载] DeepinC . Mr_zkt 集合选数
我不生产代码,我只是题解的搬运工。。。(%DeepinC %Mr_zkt)
题干:
一个有N个元素的集合有2^N个不同子集(包含空集),现在要在这2^N个集合中取出若干集合(至少一个),使得它们的交集的元素个数为K,求取法的方案数,答案模1000000007。(是质数喔~)
题解:
写题解不只是为了写题解,重在讲思路,想直接看正解的兄台自行跳跃阅读。
70%算法
虽说是暴力,但其实思路并不太好想,因为乍一下没有上正轨,所以最后的思维量几乎比std还大
然而只有70分,没人会体恤你有多笨QAQ
假如原问题的含义以函数f(n,k)表示,即n元素交集为k的方案数
我们可以认为这个过程分为两步:f(n,k)=C(n,k)*f(n-k,0)
第一步是从n个元素中选出k个C(n,k),其次要任意选集合使它们的交集恰好为这k个元素
那么我们可以确定,最后被选出的集合中,每个集合都含有这k个元素,而其余元素的交集为空集:f(n-k,0)
如:{a,b,c}三个元素,k=1时,假如你选定了a,那么可能出现的集合有:{a},{a,b},{a,c},{a,b,c}
去掉你选中的元素a,剩下的是Ø,{b},{c},{b,c},你要在这几个集合中任选,使它们交集为空
一种可能的选法是{a},{a,b},{a,b,c},去掉a后剩下Ø,{b},{b,c},它们的交集的确为空
看起来“其余元素交集为空”这个问题会更好解一些,即f(s,0)。
假如真的是这样,那么问题就化简成了f(n,k)=C(n,k)*f(n-k,0)
这样所有的问题都可以依赖f(n-k,0)来求解了。我们只需要求出f(x,0)这个数列
为了尝试找到这个数列的规律,于是我手模了许多点,得到了f(n,k)的表格
这个表格里貌似规律很多,但是f(x,0)真的有规律么:2,10,218,64594...
我没发现。。。但是我发现了最后一行:所有选法是22^n-1,因为一共2n个集合,每一个集合都可以选或不选
但不能都不选,所以要-1。
现在我们就能得到f(x,0)了,假如我们已知所有的f(1,0),f(2,0)....f(x-1,0),用总数减去∑i=1->xf(x,i)即∑i=1->xC(x,x-i)*f(x-i,0)
这样是n2的,在求f(n,k)的过程中我们可以求出所有的f(m,s)值(m<=n)
但是大多数f值都没有用,存下它们会爆内存,于是用完直接覆盖就行,保留f(x,0)即可。
1 #include<cstdio> 2 #define int long long 3 const long long mod=1000000007; 4 long long fac[1000005],inv[1000005],invv[1000005],x,y,n,k,f0[1000005],f[1000005]; 5 long long pow(long long b,long long t,long long modd,long long ans=1){ 6 for(;t;t>>=1,b=b*b%modd)if(t&1)ans=ans*b%modd; 7 return ans; 8 } 9 signed main(){ 10 scanf("%lld%lld",&n,&k); 11 fac[0]=inv[0]=invv[0]=f0[0]=invv[1]=inv[1]=fac[1]=1;f0[1]=2; 12 for(int i=2;i<=n;++i)fac[i]=fac[i-1]*i%mod,invv[i]=(-mod/i*invv[mod%i])%mod,inv[i]=inv[i-1]*invv[i]%mod; 13 for(int i=1;i<=n;++i){ 14 f0[i]=(pow(2,pow(2,i,mod-1),mod)-1+mod)%mod; 15 for(int j=1;j<=i;++j)f[j]=fac[i]*inv[j]%mod*inv[i-j]%mod*f0[i-j]%mod,f0[i]=(f0[i]-f[j]+mod)%mod; 16 f[0]=f0[i]; 17 } 18 printf("%lld\n",(f[k]+mod)%mod); 19 }
Code
100%算法1
在求f(n,k)的过程当中出现了太多的冗余运算量,肯定不是正解,死了~
不知道有没有大神能优化那个我认为没救了的算法,我是抛弃它了
然后我们还是好好看看题目吧:又是交集又是子集的,能想到什么?
呃。。这个悬念有点无趣,因为我在题目里就已经说了。。容斥嘛。。。
为什么刚开始想不到啊啊啊啊(笨,不解释)
每次我都喜欢举这个例子:4个圈的venn图
这是一张让我第二次受益匪浅的图
现在考虑n=4,k=2
每个圈分别叫{a,b,c,d},它们包含的部分都表示这些选法的交集含有这个元素
那么两个圈的公共部分就表示他们的交集含有两个元素
仔细想想每个图里要填些什么,我不方便把我的草稿纸照下来。。
假如只被一个圈包含的颜色最浅的部分叫做1级部分,两个圈的公共部分而不被其它圈包含的叫做2级部分,以此类推
最后可以发现,1级部分上都写着218,2级上是10,3级是2,4级是1。
p级是f(n-p,0)。。。唉。。。看似没什么进展,还是不会求啊
但是我们再找找别的规律:
重新定义级数,1级表示含有1个特定元素的部分(可以不是恰好只含有它),如图中3个完整的圆都是1级部分,整个红色区域也是1个1级的
对于每个4级部分,是1
对于每个3级部分,是2+1=3
对于每个2级部分,是10+2+1+2=15
对于每个1级部分,是10+2+1+2+2+10+10+218=255
对于每个s级部分,是2n-s-1
我们现在要求f(4,2),尝试用这些部分把它表示出来吧
1级部分我们貌似用不到
2级部分用得到,一共有6个2级部分,是C(4,2),把它们加上,ans=6*15=90
然而在加2级部分的时候我们误加了一些3级部分,把它们减去
有几个呢?每个2级部分都包括2个3级部分,那么一共12个,ans=ans-12*3=54
然而又一次地,我们减去过多的4级部分了,该加回来多少呢?
在加2级部分时,每个2级部分包含1个4级部分,6个2级部分一共加了6个4级部分
在减3级部分时,每个3级部分包含1个4级部分,12个3级部分一共减了12个4级部分
所以一共应该加回来6个4级部分,ans=ans+6×1=60 对了!
现在只剩下了两个问题:怎么求22^n?怎么获得一个k级部分应该被加减几次?
对于第一个问题,显然不能直接把指数对1e9+7取模,答案不对,那应该怎么办?
费马小定理:ap-1Ξp(mod p) p是质数
所以说指数是可以对p-1取模,即1e9+6
对于第二个问题就没那么好想了,至少我想的超麻烦
求f(n,k):显然k级部分需要被加C(n,k)次,为了方便研究(我懒得打),把所有数缩小这么多倍,最后再乘回来
k级:1
k+1级:-C(n-k,1)=-1(n-k)
k+2级:-C(n-k,2)+C(n-k-1,1)*C(n-k,1)=-(n-k)(n-k-1)/2+(n-k)(n-k-1)=(1/2)*(n-k)(n-k-1)
k+3级:-C(n-k,3)+C(n-k-1,2)*C(n-k,1)+C(n-k-2,1)*(-C(n-k,2)+C(n-k-1,1)*C(n-k,1))=(-1/6+1/2-1/2)(n-k)(n-k-1)(n-k-2)=(-1/6)(n-k)(n-k-1)(n-k-2)
k+4级:-C(n-k,4)+...=(-1/24+1/6-1/4+1/6)(n-k)(n-k-1)(n-k-2)(n-k-3)=(1/24)(n-k)(n-k-1)(n-k-2)(n-k-3)
红色部分的系数,是正负交替的,而其绝对值是阶乘的倒数,用逆元解决
而后面的部分显然是一段连乘,用阶乘和阶乘的逆元解决。
1 #include<cstdio> 2 const long long mod=1000000007; 3 long long fac[1000005],inv[1000005],invv[1000005],x,y,n,k,ans,res; 4 inline long long pow(long long b,long long t,long long modd,long long ans=1){ 5 for(;t;t>>=1,b=b*b%modd)if(t&1)ans=ans*b%modd; 6 return ans; 7 } 8 inline long long c(long long b,long long t){return fac[b]*inv[t]%mod*inv[b-t]%mod;} 9 signed main(){ 10 scanf("%lld%lld",&n,&k); 11 fac[0]=inv[0]=invv[0]=invv[1]=inv[1]=fac[1]=1; 12 for(int i=2;i<=n;++i)fac[i]=fac[i-1]*i%mod,invv[i]=(-mod/i*invv[mod%i])%mod,inv[i]=inv[i-1]*invv[i]%mod;res=c(n,k); 13 for(int i=k;i<=n;++i) 14 ans=(ans+(((k-i)&1)?-1:1)*res*inv[i-k]%mod*fac[n-k]%mod*inv[n-i]%mod*(pow(2,pow(2,n-i,mod-1),mod)-1))%mod; 15 printf("%lld\n",(ans+mod)%mod); 16 }
Code
100%算法2
首先,选出k个数,用C(n,k)去乘某个东西;
不妨设剩下m个数,则要在这2^m个集合中选出一定数量的集合使得它们的交集为空;然后容斥就好了
列出式子:C(n,k)×(sigma{C(m,i)×2^(2^(m-i))}(i从0到m,i为偶)-sigma{C(m,i)×2^(2^(m-i))}(i从0到m,i为奇));
dfs就可以了,中间插一些费马小定理:(2^(m-i)),快速幂:2^(2^(m-i)),逆元(组合数);
预处理出阶乘,逆元;和2的次mi对(1e9+6)取mod;
复杂度O(mlog(1e9+7));
1 #include<cstdio> 2 #include<iostream> 3 #define ll long long 4 #define p1 1000000007 5 #define p2 1000000006 6 #define N 1000009 7 using namespace std; 8 ll n,k,ans,po2[N],m,inc[N],inv[N]; 9 ll po1(ll p) 10 { 11 ll al=1,r=2; 12 for(;p;p>>=1,r=r*r%p1) 13 { 14 if(p&1)al=al*r%p1; 15 } 16 return al; 17 } 18 void dfs(ll g,ll pd) 19 { 20 if(g<0)return; 21 if(pd&1)ans-=inc[m]*inv[pd]%p1*inv[m-pd]%p1*po1(po2[g])%p1; 22 else ans+=inc[m]*inv[pd]%p1*inv[m-pd]%p1*po1(po2[g])%p1; 23 ans%=p1; 24 dfs(g-1,pd+1); 25 } 26 void gcd(ll a,ll b,ll &x,ll &y) 27 { 28 if(!b) 29 { 30 x=1,y=0; 31 return; 32 } 33 gcd(b,a%b,x,y); 34 ll t=x;x=y;y=t-a/b*y; 35 } 36 void init()//m 37 { 38 po2[0]=1; 39 inc[0]=1; 40 inv[0]=1; 41 ll y; 42 for(ll i=1;i<=m;++i) 43 { 44 po2[i]=po2[i-1]*2; 45 po2[i]=po2[i]>=p2?po2[i]-p2:po2[i]; 46 inc[i]=inc[i-1]*i%p1; 47 gcd(inc[i],p1,inv[i],y); 48 inv[i]=(inv[i]+p1)%p1; 49 } 50 for(ll i=m+1;i<=n;++i) 51 { 52 inc[i]=inc[i-1]*i%p1; 53 gcd(inc[i],p1,inv[i],y); 54 inv[i]=(inv[i]+p1)%p1; 55 } 56 } 57 int main() 58 { 59 scanf("%lld%lld",&n,&k); 60 m=n-k; 61 init(); 62 ans=0; 63 dfs(m,0); 64 ans=ans*inc[n]%p1*inv[k]%p1*inv[m]%p1; 65 cout<<(ans+p1)%p1<<endl; 66 67 }
Code
转载于:https://www.cnblogs.com/OI-zzyy/p/11112597.html
[转载] DeepinC . Mr_zkt 集合选数相关推荐
- [HNOI2012]集合选数(思维构造 + 状压dp)
problem 题目链接 solution 从最小一个数 xxx 开始,将其 2x,3x2x,3x2x,3x 放入,再将 2(2x),3(2x),2(3x),3(3x)2(2x),3(2x),2(3x ...
- [HNOI2012]集合选数 BZOJ2734
分析: 构造法...每次找到一个没有被选过的数,用这个数推出一个表格,之后在表格上跑状压DP,时间复杂度O(n) 附上代码: #include <cstdio> #include < ...
- 【BZOJ】2734: [HNOI2012]集合选数
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2734 考虑$N=4$的情况: \begin{bmatrix} 1&3 &X ...
- BZOJ 2734 [HNOI2012]集合选数 (状压DP、时间复杂度分析)
题目链接 https://www.lydsy.com/JudgeOnline/problem.php?id=2734 题解 嗯早就想写的题,昨天因为某些不可告人的原因(大雾)把这题写了,今天再来写题解 ...
- [BZOJ 2734] 集合选数
Link: BZOJ 2734 传送门 Solution: 真是奥妙重重的建模啊..... 我们发现$x,2*x,3*x$这些数太分散了,难以处理 于是我们构建这样的表格: x 3x 9x 27 ...
- P3226-[HNOI2012]集合选数【状压dp】
正题 题目链接:https://www.luogu.com.cn/problem/P3226 题目大意 1∼n1\sim n1∼n选出一些组成集合,一个集合里如果有xxx则不能有2x,3x2x,3x2 ...
- BZOJ3930: [CQOI2015]选数
BZOJ3930: [CQOI2015]选数 Description 我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案. 小z很好奇这样选出的数的最大公约数的规 ...
- 洛谷 P1036 选数
P1036 选数 题目描述 已知 n 个整数 x1,x2,-,xn,以及一个整数 k(k<n).从 n 个整数中任选 k 个整数相加,可分别得到一系列的和.例如当 n=4,k=3,4 个整数分别 ...
- 【BZOJ3930】[CQOI2015]选数 莫比乌斯反演
[BZOJ3930][CQOI2015]选数 Description 我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案.小z很好奇这样选出的数的最大公约数的规律 ...
最新文章
- 继承单例模式 php_详解PHP单例模式之继承碰见的问题
- 单指手势,旋转,缩放
- python list 查找子列_python – SQLAlchemy查询,其中列包含一个子字符串
- python能print中文吗_win10中文版,python的print不能打印中文字符?
- 27.4. /etc/bandwidthd.conf
- CKMLCP前期未结算_报错
- gitlab备份与恢复操作整理
- java java se_Java SE 11:推动Java前进
- 现在好多人做 局域网聊天
- coreos 搭建PHP,Linux_用Mac在CoreOS上搭建WordPress的教程,作者以自己的Mac笔记本为例, - phpStudy...
- python基础30个常用代码-30 个Python代码实现的常用功能,精心整理版
- 在Windows Mobile上隐藏你的应用程序
- [转]RUP (From 中科永联)
- NIK插件-托马斯教程1-color efex pro 4
- 怎样采集百家论坛MP3讲座
- Spring中的依赖注入(10级学员 韩晓爽课堂总结)
- 基于物联网技术的校园智慧消防管理平台-Susie 周
- 业务:杭州某私募基金公司商业模式
- The projects in the reactor contain a cyclic reference
- python爬虫中遇到“\xb5”、“xa0”等字符时报错编码错误的处理方式
热门文章
- 杰奇php手机登陆自动跳转,杰奇cms电脑与手机自动判断跳转代码
- 如何开发一个车牌识别,车牌识别系统,车辆识别系统毕业设计毕设作品
- 短文本分类:电力95598工单分类实现
- Dynamic Head: Unifying Object Detection Heads with Attentions 阅读
- 洋葱模型php,PHPIZE的作用 - tree2013的个人空间 - OSCHINA - 中文开源技术交流社区
- android什么叫服务器,Android系统中神秘的Bootloader究竟是什么
- windows 7下安装软件时窗口显示不全的问题
- “互联网+教育”促进优质资源共享
- 12c及以上参数推荐设置
- aoc s32p Android 内存,一机两用!AOC企鹅影霸 S32P显示器评测