组合数学中有这这样一个神奇的定理:如果对于两个数n、m,如果(n&m)==m,那么C(n,m)为奇数,否则为偶数。
这个其实很容易证明的把C(n,m)化为阶乘表示:n!/(n!*(n-m)!),如果C(n,m)为奇数除式上方和下方所含有的2的个数应该是一样的,不一样的话肯定为偶数。而n!含2的阶乘的数量其实就是看这个数一直除2然后每次结果加一下知道了,但这样所有数都计算的话复杂度其实还是很大的。那么还有其他的更优算法吗?其实一个数x的阶乘含有质因数2的个数等于x-__builtin_popcount(x),__builtin_popcount(x)为x的二进制下1的个数。
那么C(n,m)为奇数的话n-__builtin_popcount(n)=m-__builtin_popcount(m)+n-m-__builtin_popcount(n-m),n的二进制含1的个数与m,n-m的二进制含1个数的和是一样的。那么只有在(n&m)==m的时候才能满足这个条件。
HDU 4349 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=4349
题意:
给一个n,求C(n,0),C(n,1),C(n,2),……,C(n,n)中奇数的个数。
题解:
由上面的定理,我们可以很容易的解出这道题,求所有的奇数的个数只需求出n的二进制中1的个数,答案就是1<<__builtin_popcount(n).
1 #include<bits/stdc++.h> 2 using namespace std; 3 int n; 4 int main() { 5 while(~scanf("%d",&n)) { 6 int ans=__builtin_popcount(n); 7 cout << (1<<ans) << endl; 8 } 9 return 0; 10 }
HDU 6129 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6129
题意:
给一个数组a,求这个数组m次xor的前缀和
题解:
首先打个表,很容易发现第i个数在第j行的第i+k列的贡献度为C(j+k-1,j-1)次,根据XOR规律,如果为奇数次相当于一次贡献,偶数次为0次贡献。只需判断某个数的前x位的数是否存在贡献度就行了。
#include<bits/stdc++.h> using namespace std; const int maxn=2e6+100; int T,n,m,a[maxn],b[maxn]; int main() { scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); memset(b,0,sizeof(b)); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=n;++i) { int k=m+i-2,l=i-1; if((k&l)==l) for(int j=i;j<=n;++j) b[j]^=a[j-i+1]; } for(int i=1;i<=n;++i) printf("%d%c",b[i],i==n?‘\n‘:‘ ‘); } return 0; }
HDU 4349 Xiao Ming's Hope & HDU 6129 Just do it
时间: 2024-10-14 06:34:18