【题解】Rainbow的信号(位运算+概率期望)

Rainbow的信号

1 s , 128   M B 1s,128\ MB 1s,128 MB

【题目描述】

F r e d a Freda Freda 发明了传呼机之后, r a i n b o w rainbow rainbow 进一步改进了传呼机发送信息所使用的信号。由于现在是数字、信息时代, r a i n b o w rainbow rainbow 发明的信号用 N N N 个自然数表示。为了避免两个人的对话被 大坏蛋 V a r i a n t F VariantF VariantF 偷听, r a i n b o w rainbow rainbow 把对话分成 A 、 B 、 C A 、B 、C ABC 三部分,分别用 a 、 b 、 c a、b、c abc 三个密码 加密。现在 F r e d a Freda Freda 接到了 r a i n b o w rainbow rainbow 的信息,她的首要工作就是解密。 F r e d a Freda Freda 了解到,这三部 分的密码计算方式如下:

1 ∼ N 1\sim N 1N N N N 个数中,等概率地选取两个数 l 、 r l、r lr,如果 l > r l>r l>r,则交换 l 、 r l、r lr。把信号中的第 l l l 个数到第 r r r 个数取出来,构成一个数列 P P P

A A A 部分对话的密码是数列 P P P x o r xor xor 和的数学期望值。 x o r xor xor 和就是数列 P P P 中各个数异或之后得到的数; x o r xor xor 和的期望就是对于所有可能选取的 l 、 r l、 r lr,所得到的数列的 x o r xor xor 和的平均数。

B B B 部分对话的密码是数列 P P P a n d and and 和的期望,定义类似于 x o r xor xor 和 。

C C C 部分对话的密码是数列 P 的 o r or or 和的期望,定义类似于 x o r xor xor 和 。

【输入格式】

第一行一个正整数 N N N

第二行 N N N 个自然数,表示 F r e d a Freda Freda 接到的信号。

【输出格式】

一行三个实数,分别表示 x o r xor xor 和、 a n d and and 和、 o r or or 和的期望,四舍五入保留 3 3 3 位小数,相邻两个实数之间用不少于一个空格隔开。三个实数分别占该测试点 40 % 、 30 % 、 30 % 40\%、30\%、30\% 40%30%30% 的分数, 如果你的输出少于三个实数,或者你的输出不合法,将被判 0 0 0 分。

【样例输入】

2 
4 5 

【样例输出】

2.750 4.250 4.750 

【样例解释】

包含共四种可能的 l , r l,r l,r

l , r l,r l,r x o r xor xor a n d and and o r or or
1 , 1 1,1 1,1 4 4 4 4 4 4 4 4 4
1 , 2 1,2 1,2 1 1 1 4 4 4 5 5 5
2 , 1 2,1 2,1 1 1 1 4 4 4 5 5 5
2 , 2 2,2 2,2 5 5 5 5 5 5 5 5 5

以上每一对 l , r l,r l,r 出现的概率相同,因此分别对 x o r 、 a n d 、 o r xor、and、or xorandor 和取平均数,就是数学期望值。

【数据范围】

对于 20 % 20\% 20% 的数据, 1 ≤ N ≤ 100 1\le N\le 100 1N100

对于 40 % 40\% 40% 的数据, 1 ≤ N ≤ 1000 1\le N\le 1000 1N1000

对于另外 30 % 30\% 30% 的数据, N N N 个数为 0 0 0 1 1 1

对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 100000 1\le N\le 100000 1N100000 N N N 个自然数均不超过 1 0 9 10^9 109

【算法分析】

对于位运算,最后的结果只和参与运算的当前位的值有关,因此我们可以单独考虑每一位的结果,将每一位对结果的贡献相加,就得到了最后的答案。

1 ∼ n 1\sim n 1n 转换为二进制,根据题意,转化的二进制不会超过 31 31 31 位,则依次计算 1 ∼ n 1\sim n 1n 的二进制第 0 0 0 位,第 1 1 1 位… 第 30 30 30 位进行位运算的值。

for(int i=0;i<31;i++)	//二进制位
    for(int j=1;j<=n;j++)	// n个数的二进制第i位进行位运算
        	//......

l , r l,r l,r 出现的概率相同,因此, l = = r l==r l==r 出现的概率为 1 n 2 \frac 1 {n^2} n21 l ≠ r l\neq r l=r 出现的概率为 2 n 2 \frac 2 {n^2} n22

考虑 n n n 个数的第 k k k 位二进制的位运算,设 a [ i ] [ j ] a[i][j] a[i][j] 表示第 i i i 个数二进制的第 j j j 位的值。

a n d and and 和的期望:

a n d and and 的特点就是只要出现 0 0 0 结果就为 0 0 0 ,只有全为 1 1 1 时,结果才是 1 1 1 。依次计算前 r r r 个数的 a n d and and 和的期望,同时记录前 r r r 个数中 0 0 0 最后一次出现的位置, l a s t [ 0 ] = i last[0]=i last[0]=i

若第 r r r 个数的第 k k k 位为 a [ r ] [ k ] = 1 a[r][k]=1 a[r][k]=1 ,以 r r r 为右端点, [ l = l a s t [ 0 ] + 1 , r ] [l=last[0]+1, r] [l=last[0]+1,r] 这个区间任意选两个数组成的区间 a n d and and 运算都是 1 1 1 ,则对最后 a n d and and 和的期望的贡献为:

if(last[0]+1==r) a n s _ a n d + = 2 k × 1 n 2 ans\_and+=2^k\times \frac 1 {n^2} ans_and+=2k×n21

if(last[0]+1<r)​ a n s _ a n d + = 2 k × ( r − ( l a s t [ 0 ] + 1 ) ) × 2 n 2 ans\_and+=2^k\times (r-(last[0]+1))\times \frac 2 {n^2} ans_and+=2k×(r(last[0]+1))×n22

若第 r r r 个数的第 k k k 位为 a [ r ] [ k ] = 0 a[r][k]=0 a[r][k]=0 ,那么不存在 r r r 为右端点的组合使得第 k k k 位二进制进行 a n d and and 运算结果为 1 1 1

o r or or 和的期望:

o r or or 的特点就是只要有 1 1 1 ,最后的结果就是 1 1 1 ,只有全为 0 0 0 ,结果才是 0 0 0 。记录前 r r r 个数中 1 1 1 最后一次出现的位置, l a s t [ 1 ] = i last[1]=i last[1]=i

若第 r r r 个数的第 k k k 位为 a [ k ] [ r ] = 1 a[k][r]=1 a[k][r]=1,以 r r r 为右端点,前面的所有点都可以和 r r r 组成区间满足区间的数 o r or or 运算为 1 1 1 ,则对最后 o r or or 和的期望的贡献为:

[ l , r ] , l = r [l,r],l=r [l,r],l=r a n d _ o r + = 2 k × 1 n 2 and\_or+=2^k\times \frac 1 {n^2} and_or+=2k×n21

[ l , r ] , 1 ≤ l < r [l,r],1\le l<r [l,r],1l<r a n d _ o r + = 2 k × ( r − 1 ) × 2 n 2 and\_or+=2^k\times (r-1)\times \frac 2 {n^2} and_or+=2k×(r1)×n22

若第 r r r 个数的第 k k k 位为 a [ r ] [ k ] = 0 a[r][k]=0 a[r][k]=0 ,那么 r r r 为右端点, l ( 1 ≤ l ≤ l a s t [ 1 ] ) l(1\le l\le last[1]) l(1llast[1]) 组成的区间的数 o r or or 运算为 1 1 1 ,则对最后 o r or or 和的期望的贡献为:

a n d _ o r + = 2 k × l a s t [ 1 ] × 2 n 2 and\_or+=2^k\times last[1]\times \frac 2 {n^2} and_or+=2k×last[1]×n22

x o r xor xor 和的期望:

如果 l==r ,设 l   x o r   r = x l\ xor \ r=x l xor r=x 。将 l l l 往左扫描,遇到 0 0 0 ,区间 [ l , r ] [l,r] [l,r] x o r xor xor 和不变,遇到 1 1 1 ,区间 [ l , r ] [l,r] [l,r] x o r xor xor 和取反。

如下图,当第 r r r 个数的第 k k k 位为 a [ r ] [ k ] = 0 a[r][k]=0 a[r][k]=0 时,当 l l l 取灰色的位置时,区间 [ l , r ] [l,r] [l,r] x o r xor xor 和为 1 1 1 ,白色的位置时,区间 [ l , r ] [l,r] [l,r] x o r xor xor 和为 0 0 0

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WWaEUs2U-1595840188577)(img-GoAlYS9T-1595840156840)

因此,建立两个变量 c 1 , c 2 c_1,c_2 c1,c2 分别表示第从 r − 1 r-1 r1 倒着往前数的白色区域和灰色区域的长度。

a [ r ] [ k ] = 0 a[r][k]=0 a[r][k]=0 时,则左端点 l l l 只能选择灰色的部分,对最后 x o r xor xor 和的贡献的期望为:

a n s _ x o r + = 2 k × c 2 × 2 n 2 ans\_xor+=2^k\times c_2\times \frac 2 {n^2} ans_xor+=2k×c2×n22

a [ r ] [ k ] = 1 a[r][k]=1 a[r][k]=1 时,则左端点 l l l 只能选择白色的部分,对最后 x o r xor xor 和的贡献的期望为:

a n s _ x o r + = 2 k × c 1 × 2 n 2 ans\_xor+=2^k\times c_1\times \frac 2 {n^2} ans_xor+=2k×c1×n22

同时,需要加上 l==r 的情况, a n d _ x o r + = 2 k × 1 n 2 and\_xor+=2^k\times \frac 1 {n^2} and_xor+=2k×n21

r r r 所在的区域一直看作白色,左侧依次为灰色,白色,灰色…

计算 c 1 , c 2 c_1,c_2 c1,c2 时,当 r r r 增加 1 1 1c1++ ,当 a [ r ] [ k ] = 1 a[r][k]=1 a[r][k]=1 c 1 c1 c1 变为灰色区域长度, c 2 c2 c2 变为白色区域长度,交换 s w a p ( c 1 , c 2 ) swap(c_1,c_2) swap(c1,c2)

总的时间复杂度为 O ( k N ) O(kN) O(kN) k k k 为二进制位数。

在进行位运算时,可以不用将每个数拆分成二进制,可以直接使用位运算提前二进制下的每一位的值, x x x 的二进制为从有右往左依次为第 0 0 0 位,…,提取 x x x k k k 的值:(x>>k)&1

【参考程序】

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int a[N],n,last[2];
long double ans_xor,ans_or,ans_and;
void solve()
{
	for(int i=0;i<31;i++)
	{
		last[1]=last[0]=0;
		int c1=0,c2=0;
		for(int j=1;j<=n;j++)
		{
			int t=(a[j]>>i)&1;
			if(t==1)
			{
				ans_or+=(1<<i)*2.0/n/n*(j-1);		
				ans_or+=(1<<i)*1.0/n/n;
				ans_and+=(1<<i)*2.0/n/n*(j-(last[0]+1));
				ans_and+=(1<<i)*1.0/n/n;
				ans_xor+=(1<<i)*2.0/n/n*c1; 
				ans_xor+=(1<<i)*1.0/n/n;
			}
			else 
			{
				ans_or+=(1<<i)*2.0/n/n*last[1];
				ans_xor+=(1<<i)*2.0/n/n*c2;
			}
			last[t]=j;
			c1++;
			if(t==1) swap(c1,c2);
			
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]);	
	solve();
	printf("%.3Lf %.3Lf %.3Lf",ans_xor,ans_and,ans_or);
	return 0;
} 
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值