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

 

转载于:https://www.cnblogs.com/saltyfishes/p/7372745.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值