小 N N N最近学习了位运算,她发现 2 2 2个数 x o r xor xor之后数的大小可能变大也可能变小, a n d and and之后都不会变大, o r or or之后不会变 小。于是她想算出以下的期望值,现在有 N N N个数排成一排,如果她随意选择一对 l , r l,r l,r并将下标在 l l l和 r r r中间(包括 l , r l,r l,r)的数 ( x o r , a n d , o r ) (xor,and,or) (xor,and,or)之后期望得到的值是多少呢?取出每一对 l , r l,r l,r的概率都是相等的。
solution:
大概是在规定时间内
A
A
A掉了?
虽然要了个大样例
x
o
r
xor
xor和
a
n
s
,
o
r
ans,or
ans,or不太一样,所以就先写的
x
o
r
xor
xor的,但三个都是按位算
可以把这个序列的前缀异或和求出来,这样就是求随便选两个位置的值异或起来的期望,从左往右遍历这个序列,设当前点是端点的时候,因为异或只有这一位不同的时候才有贡献,所以就可以维护一个这一位是 0 / 1 0/1 0/1的数的个数,然后乘一下之类的,但是因为是前缀异或和,只选这一个数的时候情况有些许不同,所以可以仔细分类讨论一波
然后写 o r or or和 a n d and and的,这两个可以一起算,也是从前往后设当前为端点, o r or or的话只要这一位有 1 1 1那么前面都可以作为另一个端点,如果这一位是 0 0 0就看上一个这一位是 1 1 1的数在什么位置,这个也是记录一下就好了, a n d and and的话就是全部都是 1 1 1的时候才行,也就是连续段的 1 1 1才能产生贡献
总之仔细想想就好了
放代码:
#include<iostream>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<algorithm>
#define maxn 100005
#define int long long
using namespace std;
int n,a[maxn],b[maxn],cnt0,cnt1,f[35];
double ans1,ans2,ans3;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
inline void solve(){
for(int i=0;i<=30;i++){
cnt1=0; cnt0=0;
for(int j=1;j<=n;j++)
if(a[j]&(1LL<<i)){
cnt1++; cnt0=j;
ans3+=2.0*(1LL<<i)*(j-1)/(n*n)+1.0*(1LL<<i)/(n*n);
ans2+=2.0*(1LL<<i)/(n*n)*(cnt1-1)+1.0*(1LL<<i)/(n*n);
}
else {
ans3+=2.0*(1LL<<i)/(n*n)*cnt0;
cnt1=0;
}
}
}
signed main(){
freopen("nine.in","r",stdin);
freopen("nine.out","w",stdout);
n=rd();
for(int i=1;i<=n;i++) a[i]=rd(),b[i]=a[i];
for(int i=2;i<=n;i++) b[i]^=b[i-1];
for(int i=0;i<=30;i++){
cnt0=0,cnt1=0;
for(int j=1;j<=n;j++)
if(b[j]&(1LL<<i)) {
cnt1++;
if(b[j-1]&(1LL<<i) && j!=1)
ans1+=2.0*(1LL<<i)*cnt0/(n*n)+2.0*(1LL<<i)/(n*n);
else ans1+=2.0*(1LL<<i)*(cnt0-1)/(n*n)+3.0*(1LL<<i)/(n*n);
}
else {
cnt0++;
if(b[j-1]&(1LL<<i))
ans1+=2.0*(1LL<<i)*(cnt1-1)/(n*n)+1.0*(1LL<<i)/(n*n);
else ans1+=2.0*(1LL<<i)*cnt1/(n*n);
}
}
solve();
printf("%.3lf %.3lf %.3lf\n",ans1,ans2,ans3);
return 0;
}