洛谷P6102 【[EER2]谔运算】题解
思路:
我真的太弱了,看到这道题mb了半天才想出来。。。
我们先看要求什么:
i=1∑nj=1∑nk=1∑nl=1∑n(ai or aj) xor (ak and al)
在解决含有异或,与,或之类的问题时,非常常用的方法就是分位解决。
设 a[i]
化为二进制后从右至左第 j
位为 wei[i][j]
。
对于每一位,比如 pos
位。为了方便,下记 wei[i][pos]=a[i]
。设 ai 中有 x 个 1,n−x 个 0。首先原式化为
i=1∑nj=1∑nk=1∑nl=1∑n((ai or aj)+(ak and al))mod2
因为 ai只有两个取值(0 或 1)。然而
0 xor 0=1 xor 1=0
0 xor 1=1 xor 0=1
经归纳总结后,即 a xor b=(a+b)mod2。
接下来,根据 ai,aj 的取值进行分析:
1)如果 ai=aj=0。则这样的取值有 (n−x)2 种,将它提出来:
(n−x)2k=1∑nl=1∑n((0 or 0)+(ak and al))mod2
也就是
(n−x)2k=1∑nl=1∑n(ak and al)
如果,则必有 ak=al=1,有 x2 种。故最终答案为
(n−x)2x2
2)如果 ai=aj=1,则这样的取值有 x2 种,提出来得
x2k=1∑nl=1∑n(1+(ak and al))mod2
同样的,(1+(ak and al))mod2=1 时有 n2−x2 种,故原式为
x2(n2−x2)
3)如果 ai,aj 有一个是 1,一个是 0。则这样的取值有 2x(n−x) 种:
2x(n−x)k=1∑nl=1∑n(1+(ak and al))mod2
同理,化简得
2x(n−x)(n2−x2)
综上,第 pos
位的总和为
sum=x2(n−x)2+x2(n2−x2)+2x(n−x)(n2−x2)
化简后得
sum=2x(n−x)(n2−x2+nx)
故第 pos
位对答案的贡献是
sum×2pos−1
感性理解即可。关于取模,因为 pos
位对答案的贡献要乘 2pos−1,所以取模时只需要mod233−pos 即可。好了,讲解就结束了,现在是代码时间!
代码:
讲解较清楚,所以代码没有过多介绍了。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=500005,Q=33;
int n;
int wei[N][Q],cnt[Q];
int ans=0;
int pow(int x){
int kkks=1;
return kkks<<x;
}
int cal(int x){return x*x;}
signed main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
int x;
scanf("%lld",&x);
for(int j=1;j<Q;j++){
wei[i][j]=(x&1);//处理wei数组,作用详见讲解
if(wei[i][j]==1) cnt[j]++;//cnt[i]表示第i位有几个1
x>>=1;
}
}
for(int i=1;i<Q;i++){
int x=cnt[i];
int pos1=x*(n-x);pos1%=pow(Q-i);
int pos2=cal(n)-cal(x)+n*x;pos2%=pow(Q-i);
int pos=2*pos1*pos2;pos%=pow(Q-i);
//套公式
ans+=pos<<(i-1);ans%=pow(32);
//注意ans也要取模哦!
}
printf("%lld\n",ans);
}
写一篇题解不容易,看完记得点个赞哦~~
Stay hungry, stay foolish.