bzoj3054 Rainbow的信号 数学期望+位运算

Description

Freda发明了传呼机之后,rainbow进一步改进了传呼机发送信息所使用的信号。由于现在是数字、信息时代,rainbow发明的信号用N个自然数表示。为了避免两个人的对话被大坏蛋VariantF偷听T_T,rainbow把对话分成A、B、C三部分,分别用a、b、c三个密码加密。现在Freda接到了rainbow的信息,她的首要工作就是解密。Freda了解到,这三部分的密码计算方式如下:
在1~N这N个数中,等概率地选取两个数l、r,如果l>r,则交换l、r。把信号中的第l个数到第r个数取出来,构成一个数列P。
A部分对话的密码是数列P的xor和的数学期望值。xor和就是数列P中各个数异或之后得到的数; xor和的期望就是对于所有可能选取的l、r,所得到的数列的xor和的平均数。
B部分对话的密码是数列P的and和的期望,定义类似于xor和。
C部分对话的密码是数列P的or和的期望,定义类似于xor和。

solution:

首先考虑把区间长度为1的和区间长度≥2的分开处理。

长度为1的概率为1/n²,长度≥2的概率为2/n²。

所以实际上问题就转化为了:求给定序列的每一个区间的or,xor,and和。

由于位运算是不进位的,所以按位依次考虑每一位。

假设当前考虑的是第id位,那么把原序列中每一个数的第id位取出来组成一个新的01序列。

除此之外,用一个last[0/1]记录上一次出现0/1的位置。

然后考虑从左往右依次扫描这个01序列(记为b[]),如果扫到了第i位,那么:

1、对于and运算:

​ //只有二者都为1才能算贡献

​ 如果b[i]==1,那么ansand+=(1<<id)* ((i-1)-(last[0]+1)+1) *2/n/n;

​ 如果b[i]==0,显然不需要计算贡献。

2、对于or运算:

​ //有一个1就好了

​ 如果b[i]==1,那么ansor+=(1<<id)* (i-1) *2/n/n;

​ 如果b[i]==0,那么ansor+=(1<<id)* (last[1]-1) *2/n/n;

3、对于xor运算:

​ 可以发现,能够对xor造成影响是b中的每一个1。

​ 这样整个序列就被1划分成了一段一段的区间,要么1,3,5…段有贡献,要么2,4,6…段有贡献。

​ 这样的话就可以用两个变量cnt[0/1]分别记录一下上述两种区间的总长度 。

​ 如果b[i]==1,那么ansxor+=(1<<id)* cnt[0] *2/n/n;

​ 如果b[i]==0,那么ansxor+=(1<<id)* cnt[1] *2/n/n;

​ 关于cnt的更新,每次先++cnt[0],如果b[i]==1,swap(cnt[0],cnt[1])即可。

至此,本题完美解决。

#include<cmath>
#include<string>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define IL inline
#define LL long long
#define DB double
using namespace std;

IL int gi() {
   RG int x=0,w=0; char ch=getchar();
   while (ch<'0'||ch>'9') {if (ch=='-') w=1;ch=getchar();}
   while (ch>='0'&&ch<='9') x=x*10+(ch^48),ch=getchar();
   return w?-x:x;
}

const int N=1e5+1;

int n,a[N],b[N];
DB ansor,ansxor,ansand;

IL void solve(int id) {
	RG int i,las[2]={0,0},cnt[2]={0,0};
	for(i=1;i<=n;++i)
		if((b[i]=(a[i]>>id)&1)) 
			ansor+=(DB)(1<<id)/n/n,ansxor+=(DB)(1<<id)/n/n,ansand+=(DB)(1<<id)/n/n;
	for(i=1;i<=n;++i) {
		if(!b[i]) ansor+=(DB)(1<<id)*las[1]*2/n/n;
		else ansor+=(DB)(1<<id)*(i-1)*2/n/n,ansand+=(DB)(1<<id)*(i-las[0]-1)*2/n/n;
		ansxor+=(DB)(1<<id)*cnt[b[i]^1]*2/n/n;
		++cnt[0],las[b[i]]=i;
		if(b[i]) swap(cnt[0],cnt[1]);
	}		
}

int main()
{
   	RG int i;
	for(i=1,n=gi();i<=n;++i) a[i]=gi();
	for(i=0;i<32;++i) solve(i);
	printf("%.3lf %.3lf %.3lf
",ansxor,ansand,ansor);
    return 0;
}

原文地址:https://www.cnblogs.com/Bhllx/p/10808084.html