题目

给你一个数 \(n\),让你求 \(C(n,0),C(n,1),...,C(n,n)\)\(n+1\) 个数中为奇数的个数。

解法

\(\text{Update on 2021.2.15}\)

我之前写了些什么。

由卢卡斯定理得:若 \(n=a\times p+b,m=c\times p+d\)\(b,d\) 是余数),则 \(C(n,m)=C(a,c)\times C(b,d) \ (\text{mod}\ p)\)

在本题中,\(p=2\)。也就意味着 \(b,d\in [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(n,m)=C(a[0],b[0])*C(a[1],b[1])*...*C(a[lena],b[lena]) \]

因为是二进制,我们 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\n", qkpow(2, sum));
	}
	return 0;
}
posted on 2020-04-22 21:03  Oxide  阅读(108)  评论(0编辑  收藏  举报