Kor (数学题...)
来源:互联网 发布:程序员软件工程师 编辑:程序博客网 时间:2024/06/03 18:18
kor
10.19
思路:
考虑维护cnt数组,cnt[i]表示是i的数有几个。
考虑维护从cnt1数组,cnt1[i]表示是i的二进制子集的数有几个。
显然cnt1可以从cnt转移过来,但是为了优化时间复杂度,我们选择把cnt和cnt1合并为一个数组用2^20*20的时间处理出来。
代码如下
void sumup() { for(int i=0; i<P; i++) { int s = ((1<<P) - 1) ^ (1<<i); for(int ss = s; ss >= 0; ss=(ss-1)&s) { cnt[ss | (1<<i)] += cnt[ss]; //cnt[ss] += cnt[ ss| ( 1 << i ) ] ; fix(cnt[ss | (1<<i)]); if( !ss ) break; } }}
枚举每一位,把每一位为0的cnt累加到此位为1的cnt中。
对于单个的数进行考虑(如15),cnt1[15] += cnt[i] i为15的子集。
有关15的转移(每个数在加入15之前的转移,之后再怎么变化都跟15无关了)大致如上图,一看就很有道理有没有,怎么证明呢?
考虑我们for位数时是从低位往高位枚举的(枚举把哪一位的1消掉),15加上了所有在某一位上比它少1的数的cnt,如果每个数在枚举第i位时被加入了15(如11,i=3),那么在此之前它(11)一定加上了所有在某一位x(x<=i)上比它(11)少1的数的cnt(10, 9),而这些数(10, 9)是在第i-1(2)位被加入(11)的,那么在此之前它们(10, 9)一定加上了所有在某一位x(x<=i-1)上比它们(10, 9)少1的数的cnt(如cnt[9] += cnt[8])。
这样:
1.我们一定统计的数一定合法。
因为每个数统计的都是它的某一位上1变为0的cnt,所有统计到的一定是子集。
2.我们一定统计完了所有合法的数。
因为我们统计了每个数的所有后继状态(某一位上1变为0的状态)。
3.对于任意一个数我们一定没有重复统计。
我们按照1的个数将所有数进行分层操作:
15
14 13 11 7
12 10 9 6 5 3
8 4 2 1
那么显然不同层的数之间是不会相互影响的,每个数的cnt只会加到某一位上比它多1的数的cnt里。
考虑同一层的数,用第2层举例,它们加入15是有先后顺序的(从低位到高位某个1变为0)
14 1110
13 1101
11 1011
7 0111
而每一个数我们只考虑它加入15之前的变化,14在i=1(枚举的位数)时就加入15了,并没有任何的操作,13在i=2时加入15,所以x位(x < i)为0的情况已经被统计过了,容易发现一个数统计的数都是去掉某些x位上的0(x < i)得到的。也就是说y位上的数(y >= i)是不会改变的,也就是说13产生的数在第i位(i=2)上都是0,而比它大的数(14)产生的数在第i位(i=2)上都是1。而y位上的数(y >= i)一定是一样的,所以比它大的数(14)产生的数都大于它(13)产生的数,所以同层的不同数产生的数并无交集。而每个数显然不会加上多个相同的后继,所以对于任意一个数我们一定没有重复统计。
这样就能证明算法的正确性了。
可能有人觉得,15(1111)都是1太特殊,其实对于任意一个数,我们讨论子集的时候只需要管它为1的二进制位,(111100101)可直接看做(111111),只要之后的数都与它对应就好了。
还是给出关于14的转移看看吧。
处理完cnt之后,就要处理ans了。
C(cnt, k) 并不是ans。如(cnt[15], k)。
这些方案组成的(|)不只是15(1111),还会有15的子集如1101,0010等等。这里就要用到容斥的思想了。
(用二进制表示)
ans[1111] = cnt[1111] - cnt[1110] - cnt[1101] - cnt[1011] - cnt[0111] + cnt[1100] + cnt[1010] + cnt[1001] + cnt[0110] + cnt[0101] + cnt[0011] - cnt[1000] - cnt[0100] - cnt[0010] - cnt[0001]。
代码如下:
void sumdown() { for(int i=0; i<P; i++) { int s = ((1<<P) - 1) ^ (1<<i); for(int ss = s; ss >= 0; ss=(ss-1)&s) { cnt[ss | (1<<i)] -= cnt[ss]; fix(cnt[ss | (1<<i)]); if( !ss ) break; } }}
其实和上面是差不多的,只需要改成减法即可,就不再赘述啦。
还有一道kand的题目是&,只需要改成第一个代码片里//的部分就好啦。
纯手打呀~~~不容易不容易。。。(手残图丑,莫怪)
#include <iostream>#include <cstdio>#include <algorithm>#include <cstring>using namespace std;const int N = 1e5 + 10;const int Mod = 1e9 + 7;const int P = 20;int n, k, r;int aa[N];int cnt[1<<P];int fac[N], vfac[N];int mpow(int a, int b) { int rt; for(rt = 1; b; b>>=1,a=(1LL*a*a)%Mod) if(b&1) rt=(1LL*rt*a)%Mod; return rt;}void init(int n) { fac[0] = 1; for(int i = 1; i <= n; i++) fac[i] = 1LL * fac[i-1] * i % Mod; vfac[n] = mpow(fac[n], Mod - 2); for(int i = n - 1; i >= 0; i--) vfac[i] = 1LL * vfac[i+1] * (i + 1) % Mod;}int comb(int n, int m) { if(m > n) return 0; return 1LL * fac[n] * vfac[m] % Mod * vfac[n-m] % Mod;}void fix(int &a) { while(a >= Mod) a -= Mod; while(a < 0) a += Mod;}void sumup() { for(int i=0; i<P; i++) { int s = ((1<<P) - 1) ^ (1<<i); for(int ss = s; ss >= 0; ss=(ss-1)&s) { cnt[ss | (1<<i)] += cnt[ss]; //cnt[ss] += cnt[ ss| ( 1 << i ) ] ; fix(cnt[ss | (1<<i)]); if( !ss ) break; } }}void sumdown() { for(int i=0; i<P; i++) { int s = ((1<<P) - 1) ^ (1<<i); for(int ss = s; ss >= 0; ss=(ss-1)&s) { cnt[ss | (1<<i)] -= cnt[ss]; fix(cnt[ss | (1<<i)]); if( !ss ) break; } }}int main() { freopen("kor.in", "r", stdin); freopen("kor.out", "w", stdout); int T; scanf("%d", &T); init(1e5); while(T--) { memset(cnt, 0, sizeof(cnt)); scanf("%d%d%d", &n, &k, &r); for(int i = 1; i <= n; i++) { scanf("%d", aa + i); cnt[aa[i]]++; } sumup(); for(int s=0; s<(1<<P); s++) cnt[s] = comb(cnt[s], k); sumdown(); printf("%d\n", cnt[r]); } return 0;}
- Kor (数学题...)
- [bzoj2081][POI2010]KOR-Beads(hash)
- poj1152 数学题(进制位)
- hdu 2178 (数学题)
- poj 1019(数学题)
- 糖果传递 (数学题)
- cf(cards)数学题
- hdu 2832(数学题)
- 数学题(1)
- HDU5100 Chessboard(数学题)
- nyoj94cigarettes(数学题)
- 无尽弹珠(数学题)
- uva11237(数学题)
- hdu2421(数学题)
- Lightoj 1045 (数学题)
- Lightoj 1282 (数学题)
- hdu2803(数学题)
- hdu2802(数学题)
- SSDT Hook实现内核级的进程保护
- LeetCode中Reverse Integer
- angular学习总结八-请求service封装
- 条款21:必须返回对象时,别妄想返回其reference
- Linux进程间通讯--管道(有名管道
- Kor (数学题...)
- javaee 中不同页面传参方法
- 特定需求下动态代理导致的Spring事务不能回滚
- 139. Word Break
- Adobe Dreamweaver CC 2018 v18.0官方中文版下载附安装教程
- Andrew Ng's deeplearning Course1Week2 Neural Networks Basics(神经网络基础)
- 初认hadoop
- Android中GridView
- 条款22:将成员变量声明为private