洛谷P6102 【[EER2]谔运算】题解

思路:

我真的太弱了,看到这道题mb了半天才想出来。。。

我们先看要求什么:

i=1nj=1nk=1nl=1n(ai or aj) xor (ak and al)\sum\limits_{i=1}^n\sum\limits_{j=1}^n\sum\limits_{k=1}^n\sum\limits_{l=1}^n (a_i\ \text{or}\ a_j)\ \text{xor}\ (a_k\ \text{and}\ a_l)

在解决含有异或,与,或之类的问题时,非常常用的方法就是分位解决

a[i] 化为二进制后从右至左第 j 位为 wei[i][j]

对于每一位,比如 pos 位。为了方便,下记 wei[i][pos]=a[i]。设 aia_i 中有 xx11nxn-x00。首先原式化为

i=1nj=1nk=1nl=1n((ai or aj)+(ak and al))mod2\sum\limits_{i=1}^n\sum\limits_{j=1}^n\sum\limits_{k=1}^n\sum\limits_{l=1}^n ((a_i\ \text{or}\ a_j)+(a_k\ \text{and}\ a_l))\mod{2}

因为 aia_i只有两个取值(0011)。然而

0 xor 0=1 xor 1=00\ \text{xor}\ 0=1\ \text{xor}\ 1=0

0 xor 1=1 xor 0=10\ \text{xor}\ 1=1\ \text{xor}\ 0=1

经归纳总结后,即 a xor b=(a+b)mod2a\ \text{xor}\ b=(a+b)\mod{2}

接下来,根据 ai,aja_i,a_j 的取值进行分析:

1)如果 ai=aj=0a_i=a_j=0。则这样的取值有 (nx)2(n-x)^2 种,将它提出来:

(nx)2k=1nl=1n((0 or 0)+(ak and al))mod2(n-x)^2 \sum\limits_{k=1}^n\sum\limits_{l=1}^n((0\ \text{or}\ 0)+(a_k\ \text{and}\ a_l))\mod{2}

也就是

(nx)2k=1nl=1n(ak and al)(n-x)^2 \sum\limits_{k=1}^n\sum\limits_{l=1}^n(a_k\ \text{and}\ a_l)

如果,则必有 ak=al=1a_k=a_l=1,有 x2x^2 种。故最终答案为

(nx)2x2(n-x)^2x^2

2)如果 ai=aj=1a_i=a_j=1,则这样的取值有 x2x^2 种,提出来得

x2k=1nl=1n(1+(ak and al))mod2x^2 \sum\limits_{k=1}^n\sum\limits_{l=1}^n(1+(a_k\ \text{and}\ a_l))\mod2

同样的,(1+(ak and al))mod2=1(1+(a_k\ \text{and}\ a_l))\mod2=1 时有 n2x2n^2-x^2 种,故原式为

x2(n2x2)x^2(n^2-x^2)

3)如果 ai,aja_i,a_j 有一个是 11,一个是 00。则这样的取值有 2x(nx)2x(n-x) 种:

2x(nx)k=1nl=1n(1+(ak and al))mod22x(n-x)\sum\limits_{k=1}^n\sum\limits_{l=1}^n(1+(a_k\ \text{and}\ a_l))\mod2

同理,化简得

2x(nx)(n2x2)2x(n-x)(n^2-x^2)

综上,第 pos 位的总和为

sum=x2(nx)2+x2(n2x2)+2x(nx)(n2x2)sum=x^2(n-x)^2+x^2(n^2-x^2)+2x(n-x)(n^2-x^2)

化简后得

sum=2x(nx)(n2x2+nx)sum=2x(n-x)(n^2-x^2+nx)

故第 pos 位对答案的贡献是

sum×2pos1sum\times 2^{pos-1}

感性理解即可。关于取模,因为 pos 位对答案的贡献要乘 2pos12^{pos-1},所以取模时只需要mod233pos\mod{2^{33-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.