HDU 4349 Xiao Ming's Hope & HDU 6129 Just do it

组合数学中有这这样一个神奇的定理:如果对于两个数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 }
View Code

 

 

 

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;
}
View Code

 

posted on 2017-08-16 15:04  仰望咸鱼Orzzzz  阅读(217)  评论(0编辑  收藏  举报

导航