1. 组合数
【问题描述】
定义C(N,K)表示从N个元素中不重复地选取K个元素的方案数。判断C(N,K)的奇偶性?
【输入】
第1行:一个正整数t,表示数据的组数。
第2~2+t-1行:两个非负整数N和K。(保证k<=n)
【输出】
每一组输入,如果C(N,K)是奇数则输出1,否则输出0。
【输入输出样例】
parity.in |
parity.out |
3 1 1 1 0 2 1 |
1 1 0 |
【数据规模】
对于30% 的数据,n<=10^2 t<=10^4
对于50% 的数据,n<=10^3 t<=10^5
对于100%的数据,n<=10^8 t<=10^5
预备知识
C(n, 0) = C(n, n)
= 1 (n > 0;)
C(n, k) = C(n − 1, k − 1) + C(n − 1, k) (0 < k < n)
算法一
有了预备知识里面的公式,我们可以得到一个最简单的算法。
先预处理算出所有的C(n,k)。
然后再读入,输出。
但是这样会产生一个问题,当n比较大的时候,C(n,k)会超出longint的范围。
解决方法?高精度!。
由于高精度的速度限制,所以这个算法只能过30%的点。
算法二
观察题目输出,其实就是输出C(n,k)mod 2=.这样可以运用同余算术的性质。
(a + b)mod k=((a mod k)+(b mod k))mod k
(由于是mod 2,还可以写成and 1,利用位运算加速)
原式变为:
C(n, k) = (C(n − 1, k − 1) + C(n − 1, k)) mod 2 (0 < k < n)
这样好多了,可以过50%的点。
算法三
直接的递推算法还可以优化嘛?找不出了。那我们掉换思路,从规律入手。
将前面的情况全部打印出来,我们可以发现。
其实就是一个2^k*2^k的三角形分解为四个2^(k-1)*2^(k-1)的三角形,中间一个三角形全为0,
余下三个三角形继续分解。详见程序和下面的附表
这个规律比较严格的描述是这样的,设N=XaXa-1Xa-2…X1(2),K=YbYb-1Yb-2…Y1(2)
(即用二进制表示)C(n,k)为偶的充分必要条件是存在i使得Xi<Yi
这样可以通过100%的点。
附表:n=32的情况(注:为了整齐0写成00,1写成11)
11
1111
110011
11111111
1100000011
111100001111
11001100110011
1111111111111111
110000000000000011
11110000000000001111
1100110000000000110011
111111110000000011111111
11000000110000001100000011
1111000011110000111100001111
110011001100110011001100110011
11111111111111111111111111111111
1100000000000000000000000000000011
111100000000000000000000000000001111
11001100000000000000000000000000110011
1111111100000000000000000000000011111111
110000001100000000000000000000001100000011
11110000111100000000000000000000111100001111
1100110011001100000000000000000011001100110011
111111111111111100000000000000001111111111111111
11000000000000001100000000000000110000000000000011
1111000000000000111100000000000011110000000000001111
110011000000000011001100000000001100110000000000110011
11111111000000001111111100000000111111110000000011111111
1100000011000000110000001100000011000000110000001100000011
111100001111000011110000111100001111000011110000111100001111
11001100110011001100110011001100110011001100110011001100110011
1111111111111111111111111111111111111111111111111111111111111111
110000000000000000000000000000000000000000000000000000000000000011
对组合数C(n,k)
(n>=k):将n,k分别化为二进制,若某二进制位对应的n为0,而k为1 ,则C(n,k)为偶数;否则为奇数。
组合数的奇偶性判定方法为:
结论:
对于C(n,k),若n&k == k 则c(n,k)为奇数,否则为偶数。
证明:
利用数学归纳法:
由C(n,k) =
C(n,k-1) + C(n-1,k-1);
对应于杨辉三角:
1
1 2 1
1 3 3 1
1 4 6 4 1
………………
可以验证前面几层及k = 0时满足结论,下面证明在C(n-1,k)和C(n-1,k-1) (k >
0) 满足结论的情况下,
C(n,k)满足结论。
1).假设C(n-1,k)和C(n-1,k-1)为奇数:
则有:(n-1)&k
== k;
(n-1)&(k-1)
== k-1;
由于k和k-1的最后一位(在这里的位指的是二进制的位,下同)必然是不同的,所以n-1的最后一位必然是1
。
现假设n&k == k。
则同样因为n-1和n的最后一位不同推出k的最后一位是1。
因为n-1的最后一位是1,则n的最后一位是0,所以n&k != k,与假设矛盾。
所以得n&k != k。
2).假设C(n-1,k)和C(n-1,k-1)为偶数:
则有:(n-1)&k
!= k;
(n-1)&(k-1)
!= k-1;
现假设n&k == k.
则对于k最后一位为1的情况:
此时n最后一位也为1,所以有(n-1)&(k-1) == k-1,与假设矛盾。
而对于k最后一位为0的情况:
则k的末尾必有一部分形如:10; 代表任意个0。
相应的,n对应的部分为: 1{*}*; *代表0或1。
而若n对应的{*}*中只要有一个为1,则(n-1)&k == k成立,所以n对应部分也应该是10。
则相应的,k-1和n-1的末尾部分均为01,所以(n-1)&(k-1) == k-1 成立,与假设矛盾。
所以得n&k != k。
由1)和2)得出当C(n,k)是偶数时,n&k != k。
3).假设C(n-1,k)为奇数而C(n-1,k-1)为偶数:
则有:(n-1)&k
== k;
(n-1)&(k-1)
!= k-1;
显然,k的最后一位只能是0,否则由(n-1)&k == k即可推出(n-1)&(k-1) == k-1。
所以k的末尾必有一部分形如:10;
相应的,n-1的对应部分为: 1{*}*;
相应的,k-1的对应部分为: 01;
则若要使得(n-1)&(k-1)
!= k-1 则要求n-1对应的{*}*中至少有一个是0.
所以n的对应部分也就为 : 1{*}*; (不会因为进位变1为0)
所以 n&k = k。
4).假设C(n-1,k)为偶数而C(n-1,k-1)为奇数:
则有:(n-1)&k
!= k;
(n-1)&(k-1)
== k-1;
分两种情况:
当k-1的最后一位为0时:
则k-1的末尾必有一部分形如: 10;
相应的,k的对应部分为 : 11;
相应的,n-1的对应部分为 : 1{*}0; (若为1{*}1,则(n-1)&k == k)
相应的,n的对应部分为 : 1{*}1;
所以n&k = k。
当k-1的最后一位为1时:
则k-1的末尾必有一部分形如: 01; (前面的0可以是附加上去的)
相应的,k的对应部分为 : 10;
相应的,n-1的对应部分为 : 01; (若为11,则(n-1)&k == k)
相应的,n的对应部分为 : 10;
所以n&k = k。
由3),4)得出当C(n,k)为奇数时,n&k = k。