1.题意
给出一组数,求所有连续子区间xor,or,and和的平均值。
对于区间端点,任意等概率的取:
l!=r,有两次情况会被取到;l==r,只有一次情况会被取到。
共有n*n种情况;
2.思路
因为是xor,or,and这三种运算,都是位运算,二进制每个位置上的情况是独立的,所以考虑求二进制各个位置上对答案的贡献。
对于右端点r,数是a[r],枚举他的二进制上每一位,下面讨论的是第k位上的值v。
如果一段区间(l,r)在第k位上对答案有贡献:
l!=r 2^(k+1)/(n*n);
l=r 2^(k)/(n*n);
xor:
一段区间[l,r]只有在k位上有奇数个1时对答案有贡献;
如果有偶数个1时,对答案的贡献为0;
所以我设z[k]为【[1,r],r】中有多少个区间是奇数个1;
当v=1时,z[k]=r-z[k],对答案的贡献是((z[k]-1)*(2^(k+1))+2^k)/(n*n);
当v=0时,z[k]=z[k],对答案贡献是2*(k+1)*z[k]/(n*n);
and:
一段区间只有在第k位上是全为1,才对答案有贡献;
所以我用下x[k]记录当前最后一次出现0的位置;
当v=1,x[k]=x[k],对答案贡献为((r-x[k]-1)*2^(k+1)+2^k)/(n*n);
当v=0,x[k]=r,对答案贡献为0;
or:
一段区间只要有一个1时,就对答案有贡献;
所以我记录最后一次出现1的位置;
v=1,y[k]=r,对答案贡献((y[k]-1)*2^(k+1)+2^k)/n*n;
v=0,y[k]=y[k],对答案贡献y[k]*2^(k+1)/n*n;
3.代码:
#include<bits/stdc++.h>
using namespace std;
#define LL long long
const int N=1e5+100;
const int MAX=1e9;
long double sum1,sum2,sum3;
LL x[33],y[33],z[33],a[N];
LL n;
int main(){
cin>>n;
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
for(int r=1;r<=n;r++)
for(int k=0;k<31;k++)
{ int v=(a[r]>>k)&1;
if(v){
z[k]=r-z[k];
sum1+=1.0*((1<<k)+(z[k]-1)*(1<<(k+1)))/(n*n);
sum2+=1.0*((r-x[k]-1)*(1<<(k+1))+(1<<k))/(n*n);
y[k]=r;
sum3+=1.0*((y[k]-1)*(1<<(k+1))+(1<<k))/(n*n);
}
else{
sum1+=1.0*z[k]*(1<<(k+1))/(n*n);
x[k]=r;
sum3+=1.0*(y[k])*(1<<(k+1))/(n*n);
}
}
printf("%.3Lf %.3Lf %.3Lf\n",sum1,sum2,sum3);
return 0;
}