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\)直接通过这一位来枚举答案,但是似乎并不是这样,而且自己推式子的时候不能想当然,要一步一步的推过去

浙公网安备 33010602011771号