题目
给你一个数 (n),让你求 (C(n,0),C(n,1),...,C(n,n)) 这 (n+1) 个数中为奇数的个数。
解法
( ext{Update on 2021.2.15})
我之前写了些什么。
由卢卡斯定理得:若 (n=a imes p+b,m=c imes p+d)((b,d) 是余数),则 (C(n,m)=C(a,c) imes C(b,d) ( ext{mod} p))。
在本题中,(p=2)。也就意味着 (b,din [0,1]),所以 (C(b,d)) 只有 (4) 种组合,且当且仅当 (b=0,d=1) 时取 (0)。
由于将 (n,m) 不断除以 (2),实际上是对 (n,m) 进行 (2) 进制分解。当某一位 (n) 是 (0),(m) 是 (1) 时 (C(b,d)=0) 即 (C(n,m)=0)(在模意义下)。
(n) 是固定的,如果 (n) 这一位为 (1) 对于 (m) 就有两种选择,而为 (0) 就只有一种选择(即保证 (m) 二进制分解后是 (n) 的子集)。
我们用 (sum) 来统计 (n) 为 (1) 的个数,答案就是 (pow(2,sum))。
旧版本
首先,我们知道这是一道 (Lucas)。开 卷 考 试
我们发现,题目中要求的是 (sum_{i=0}^{n}C(n,i)\%2)。
贴出这个玩意儿的代码:
int Lucas(const int n, const int m) {
if(n < m) return 0;
if(! m) return 1;
return Lucas(n / p, m / p) * C(n % p, m % p) % p;
}
我们设 n 的二进制的每一位可以表示成数组 a,m 则表示成数组 b。可以给出一个式子:
因为是二进制,我们 C 可能的组合就非常有限:
- (C(1,1)=1)。
- (C(1,0)=1)。
- (C(0,1)=0)。
- (C(0,0)=1)。
我们的 n 的二进制是固定的。由此可见,如果要使 (C(n,m)) 为 1,分解出来的 C 就一定要是 1。如果 n 这一位为 1 对于 m 就有两种选择,而为 0 就只有一种选择。
我们用 (sum) 来统计 n 为 1 的个数,再用 (pow(2,sum)) 即为答案。
注意这里直接统计的前提:(m<=n)。仔细观察前面的式子,发现统计的情况一定会合法(即 m 二进制每一位都小于等于 n 二进制对应的位)。
代码
#include <cstdio>
int n, sum;
int read() {
int x = 0, f = 1; char s;
while((s = getchar()) > '9' || s < '0') if(s == '-') f = -1;
while(s <= '9' && s >= '0') {
x = (x << 1) + (x << 3) + (s ^ 48);
s = getchar();
}
return x * f;
}
int qkpow(int x, int y) {
int r = 1;
while(y) {
if(y & 1) r = r * x;
x = x * x; y >>= 1;
}
return r;
}
int main() {
while(~ scanf("%d", &n)) {
sum = 0;
while(n) {
if(n & 1) ++ sum;
n >>= 1;
}
printf("%d
", qkpow(2, sum));
}
return 0;
}