期望+位运算--连续段的期望

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值