hdu3929

二项式定理有两个性质,第一个是高中就接触过的,即奇数项(这里的奇数项与题目的奇数项定义不同)和偶数项系数之和相同

第二个也可以记住

阶乘分解质因数注意复习一下

这里拓展一个:$$\lfloor \frac{\lfloor\frac{n}{p}\rfloor}{q} \rfloor=\lfloor\frac{n}{pq}\rfloor$$

证明:设\(n=kp+r(0≤r<p)\),则\(LHS=\lfloor\frac{k}{q}\rfloor\)\(RHS=\lfloor\frac{k}{q}+\frac{r}{pq}\rfloor\);有\(\frac{r}{pq}<\frac{1}{q}\),我们将\(\frac{k}{q}\)看成\(k\)\(\frac{1}{q}\)相加,将数轴上的每两个整数之间划分成\(q\)个均匀段,所以\(k\)\(\frac{1}{q}\)相加会停留在某一个点,再加上一个小于\(\frac{1}{q}\)的数显然不会到达下一个位置。画图如下

当然这也告诉我们:中途一旦产生进位了,那么\(1\)的个数就要变化了

为什么?我们从竖式加法的角度考虑。从低位往高位进行加法,第一次进行的时候一定是两个\(1\)合成了一个\(0\),此时\(bitcnt(k)+bitcnt(n-k)\)\(bitcnt(n)\)要多\(2\),然后进了一位,如果下一位是\(0+0\),那么\(bitcnt(k)+bitcnt(n-k)\)\(bitcnt(n)\)\(1\);如果下一位是\(0+1\)或者\(1+0\),那么继续进位并且\(bitcnt(k)+bitcnt(n-k)\)\(bitcnt(n)\)要多\(3\);如果下一位是\(1+1\),那么\(bitcnt(k)+bitcnt(n-k)\)\(bitcnt(n)\)要多\(3\)。然后继续下去就会发现\(bitcnt(k)+bitcnt(n-k)\)一定比\(bitcnt(n)\)要多

为什么这里是覆盖奇数次?因为偶数不改变奇偶性

这个典型的容斥原理简单来说,就是先求每个\(a_i\)的子集,然后再求每两个\(a_i\)交集的子集,但是这里不是直接减掉,是要减掉两倍这么多,然后再求每三个\(a_i\)交集的子集,但是是加上四倍这么多,以此类推,下面以三个集合的文氏图来理解

先求每个\(a_i\)的子集

再减掉每两个\(a_i\)的交集的两倍

再加上三个\(a_i\)的交集的四倍

很显然就是被覆盖了奇数次的集合

由于这道题目只用统计个数,我们没有必要每次去算出具体的集合,而是可以直接累加贡献,所以时间复杂度为\(O(2^m)\),具体见代码

代码可以看看这篇文章

这篇文章的代码:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<iomanip>
#include<cmath>
#include<cstring>
#include<vector>
#define ll __int64
#define pi acos(-1.0)
#define MAX 50000
using namespace std;
ll an[16],ans;
int n;
int get(ll n)
{
    int t=0;
    while(n){
        n-=(n&-n);
        t++;
    }
    return t;
}
void dfs(int i,ll sum,ll k)//递归版容斥原理
{
    ans+=(1ll<<(get(sum)))*k;
    for(int j=i+1;j<n;j++)
        dfs(j,sum&an[j],-2*k);
}
ll solve(int n)//迭代版容斥原理
{
    ll ans=0,res,m=1;
    int i,j,temp;
    for(i=1;i<(1<<n);i++){
        temp=0;
        for(j=0;j<n;j++){
            if(i&(1<<j)){
                temp++;
                if(temp==1) res = an[j];
                else res=res&an[j];
            }
        }
        res=(1<<get(res));
        ans+=m*res;
        m*=-2;
    }
    return ans;
}
int main(){
    int i,t,k=1;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        for(i=0;i<n;i++)
            scanf("%I64d",&an[i]);
        ans=0;
        for(i=0;i<n;i++)
            dfs(i,an[i],1);
        printf("Case #%d: %I64d\n",k++,ans);
    }
    return 0;
}
posted @ 2024-02-18 10:55  最爱丁珰  阅读(13)  评论(0)    收藏  举报