异或和之和(组合数学)

异或和之和(组合数学)

传送门

思路:考虑每位对答案的贡献,因为最大为 2 18 2^{18} ,所以最大一共64位。

储存每一个1的个数,贡献产生只能出现两种情况

p o s 1 : pos1: 一个1,两个0.

p o s 2 : pos2: 三个1。

然后用组合数和加法原理对贡献求和即可。

即第 i i 位的贡献 a n s i = C ( a [ i ] , 3 ) + C ( n a [ i ] , 2 ) × a [ i ] ans_i=C(a[i],3)+C(n-a[i],2)\times a[i]

p s : ps: 计算组合数除法时需要用到逆元。

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+5,mod=1e9+7;
int a[100];
ll ksm(ll a,ll n){
    ll ans=1;
    while(n){
       if(n&1) ans=ans*a%mod;
        a=a*a%mod;
        n>>=1;
    }
    return ans;
}
ll inv(ll x){
    return ksm(x,mod-2);
}
ll f(ll x,ll y){
    if(x<y) return 0;
    ll ans=1;
    for(int i=0;i<y;i++)
        ans=ans*(x-i)%mod,ans=ans*inv(i+1)%mod;
    return ans;
}
int main(){
    int n ;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        ll x,j=0;
        scanf("%lld",&x);
        while(x){
            if(x&1) a[j]++;
            x>>=1,j++;
        }}
    ll ans=0;
    for(int i=0;i<64;i++){
       ans+=(1LL<<i)%mod*(f(a[i],3)+f(n-a[i],2)*a[i]%mod)%mod;
       ans%=mod;
    }
    printf("%lld\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45750972/article/details/106186365