小NNN最近学习了位运算,她发现222个数xorxorxor之后数的大小可能变大也可能变小,andandand之后都不会变大,ororor之后不会变 小。于是她想算出以下的期望值,现在有NNN个数排成一排,如果她随意选择一对l,rl,rl,r并将下标在lll和rrr中间(包括l,rl,rl,r)的数 (xor,and,or)(xor,and,or)(xor,and,or)之后期望得到的值是多少呢?取出每一对l,rl,rl,r的概率都是相等的。
solution:
大概是在规定时间内AAA掉了?
虽然要了个大样例
xorxorxor和ans,orans,orans,or不太一样,所以就先写的xorxorxor的,但三个都是按位算
可以把这个序列的前缀异或和求出来,这样就是求随便选两个位置的值异或起来的期望,从左往右遍历这个序列,设当前点是端点的时候,因为异或只有这一位不同的时候才有贡献,所以就可以维护一个这一位是0/10/10/1的数的个数,然后乘一下之类的,但是因为是前缀异或和,只选这一个数的时候情况有些许不同,所以可以仔细分类讨论一波
然后写ororor和andandand的,这两个可以一起算,也是从前往后设当前为端点,ororor的话只要这一位有111那么前面都可以作为另一个端点,如果这一位是000就看上一个这一位是111的数在什么位置,这个也是记录一下就好了,andandand的话就是全部都是111的时候才行,也就是连续段的111才能产生贡献
总之仔细想想就好了
放代码:
#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;
}