CF1466E Apollo versus Pan 题解

题意

计算

\[\sum_{i=1}^{n}\sum_{j=1}^{n}\sum_{k=1}^n (x_i \& x_j)(x_j | x_k) \]

其中\(n \leq 5 \times 10^5\)

分析

观察这个式子,发现要求的那个东西左右两端都出现了\(j\),所以可以尝试把\(j\)提到前边枚举,然后考虑怎么优化后边的式子。
提出\(j\)后,这个式子就变成了

\[\sum_{j=1}^{n}(\sum_{i=1}^{n} (x_i \& x_j)\sum_{k=1}^{n} (x_j | x_k)) \]

现在的问题就只剩下了怎么快速求所有数与一个数字的&和|了
按位考虑,发现

\[\sum_{i=1}^n(x_i \& v)=\sum_{w=0}(1<<w)\sum_{i=1}^{n}((x_i>>w)\&(v>>w)) \]

即我们可以按位考虑,记录按位下的每一位的前缀和然后判断相加最后把乘起来即可。

代码


// Created on iPad.

#include <bits/stdc++.h>
using namespace std;
const int P=1e9+7;
int main(){
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        scanf("%d",&n);
        vector<int> sum(61);
        vector<long long> a(n+1);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
            for(long long j=0;j<=60;j++)
                sum[j]+=(a[i]>>j)&1;
        }
        long long ans=0;
        for(int i=1;i<=n;i++){
            long long l=0,r=0;
            for(long long j=0;j<=60;j++){
                if((a[i]>>j)&1){
                    l=(l+(1ll<<j)%P*sum[j]%P)%P;
                    r=(r+(1ll<<j)%P*n%P)%P;
                }
                else 
                    r=(r+(1ll<<j)%P*sum[j]%P)%P;
            }
            ans=(ans+l*r%P)%P;
        }
        printf("%lld\n",ans); 
    }
    return 0;
}

心得

其实之前好像口胡过这道题但是并没有写,所以开始的时候虽然式子推出来了,但是求的时候遇到了很大的问题,刚开始的时候想先枚举位,然后再枚举\(j\)直接通过这一位来枚举答案,但是似乎并不是这样,而且自己推式子的时候不能想当然,要一步一步的推过去

posted @ 2021-10-19 15:06  兮水XiShui  阅读(44)  评论(0)    收藏  举报