【JoyOI 2020】Rainbow的信号

本文介绍了一种计算数列在不同长度子序列下位运算(包括异或、与、或运算)数学期望值的高效算法。通过对每个二进制位独立分析,并利用滑动窗口技巧,实现了快速求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门


Problem

给出 n n n 个数,等概率地选取两个数 l l l r r r(如果 l > r l>r l>r,则交换 l l l r r r),把第 l l l 个数到第 r r r 个数取出来,构成一个数列 P \mathrm P P,现在请你计算三个值 a , b , c a,b,c a,b,c

a a a 是数列 P \mathrm P P x o r \mathrm{xor} xor 和的数学期望值。 x o r \mathrm{xor} xor 和就是数列 P \mathrm P P 中各个数异或之后得到的数; x o r \mathrm{xor} xor 和的期望就是对于所有可能选取的 l l l r r r,所得到的数列的 x o r \mathrm{xor} xor 和的平均数。

b b b 是数列 P \mathrm P P a n d \mathrm{and} and 和的期望,定义类似于 x o r \mathrm{xor} xor 和。

c c c 是数列 P \mathrm P P o r \mathrm {or} or 和的期望,定义类似于 x o r \mathrm{xor} xor 和。

数据范围: 1 ≤ n ≤ 100000 1\le n\le100000 1n100000 n n n 个自然数均不超过 1 0 9 10^9 109


Solution

很好的一道题。

由于位运算是按位计算,各个位之间互不影响,所以我们可以把每个位拆开单独考虑。

按照题目中的选择方式,长度为 1 1 1 的序列(即 l = r l=r l=r)被选出来的概率为 1 n 2 \frac 1 {n^2} n21,其他的都是 2 n 2 \frac 2 {n^2} n22。因此,对于序列的每一个数,如果第 k k k 位是 1 1 1,就先累加 2 k × 1 n 2 2^k\times\frac 1 {n^2} 2k×n21 进答案。那么接下来就只用考虑长度 > 1 >1 >1 的序列了。

我们枚举右端点 r r r,用 l a s t k ( k = 0 , 1 ) last_k(k=0,1) lastk(k=0,1) 表示 k k k 上一次出现的位置。

对于 a n d \mathrm{and} and

显然只有当第 r r r 位是 1 1 1,且 l ∈ [    l a s t 0 + 1 , r − 1    ] l\in[\;last_0+1,r-1\;] l[last0+1,r1] a n d \mathrm{and} and 1 1 1

所以第 r r r 位是 1 1 1 时,就累加 2 k × ( r − l a s t 0 − 1 ) × 2 n 2 2^k\times(r-last_0-1)\times \frac{2}{n^2} 2k×(rlast01)×n22

对于 o r \mathrm{or} or

若第 r r r 位是 1 1 1,那 l ∈ [    1 , r − 1    ] l\in[\;1,r-1\;] l[1,r1] o r \mathrm{or} or 都是 1 1 1,此时累加 2 k × ( r − 1 ) × 2 n 2 2^k\times(r-1)\times \frac{2}{n^2} 2k×(r1)×n22

若第 r r r 位不是 1 1 1,那当 l ∈ [    1 , l a s t 1    ] l\in[\;1,last_1\;] l[1,last1] o r \mathrm{or} or 1 1 1,此时累加 2 k × l a s t 1 × 2 n 2 2^k\times last_1\times \frac{2}{n^2} 2k×last1×n22

对于 x o r \mathrm{xor} xor

如果我们枚举 l l l 从做左往右扫描,当遇到 0 0 0 x o r \mathrm{xor} xor 不变,遇到 1 1 1 x o r \mathrm{xor} xor 取反。

那我们可以以 1 1 1 作为分界,把序列分为几段,在同一段取 l l l x o r \mathrm{xor} xor 的值相同,相邻段取 l l l x o r \mathrm{xor} xor 相反。

我们建立两个变量 t 1 , t 2 t_1,t_2 t1,t2 t 1 t_1 t1 记录从 r − 1 r-1 r1 数起,第 1 , 3 , 5 , . . . 1,3,5,... 1,3,5,... 段的总长; t 2 t_2 t2 记录第 2 , 4 , 6 , . . . 2,4,6,... 2,4,6,... 段的总长。

当第 r r r 位是 1 1 1 时, t 1 t_1 t1 是能使 x o r \mathrm{xor} xor 1 1 1 的数的个数,那么此时累加 2 k × t 1 × 2 n 2 2^k\times t_1\times\frac{2}{n^2} 2k×t1×n22

同理,当第 r r r 位是 0 0 0 时, t 2 t_2 t2 是能使 x o r \mathrm{xor} xor 1 1 1 的数的个数,此时累加 2 k × t 2 × 2 n 2 2^k\times t_2\times\frac{2}{n^2} 2k×t2×n22

当右端点变为 r + 1 r+1 r+1 时,令 t 1 = t 1 + 1 t_1=t_1+1 t1=t1+1,若 r + 1 r+1 r+1 位是 1 1 1,还要交换 t 1 , t 2 t_1,t_2 t1,t2


Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
using namespace std;
int n,a[N];
double Xor(){
	int i,j;
	double ans=0;
	for(j=0;j<31;++j){
		int t1=0,t2=0;
		for(i=1;i<=n;++i){
			if(a[i]&(1<<j))  ans+=1.0*(1<<j)*t1*2.0/n/n;
			else  ans+=1.0*(1<<j)*t2*2.0/n/n;
			t1++;  if(a[i]&(1<<j))  swap(t1,t2);
		}
	}
	return ans;
}
double And(){
	int i,j;
	double ans=0;
	for(j=0;j<31;++j){
		int last0=0;
		for(i=1;i<=n;++i){
			if(a[i]&(1<<j))
			  ans+=1.0*(1<<j)*(i-last0-1)*2.0/n/n;
			if(!(a[i]&(1<<j)))  last0=i;
		}
	}
	return ans;
}
double Or(){
	int i,j;
	double ans=0;
	for(j=0;j<31;++j){
		int last1=0;
		for(i=1;i<=n;++i){
			if(a[i]&(1<<j))  ans+=1.0*(1<<j)*(i-1)*2.0/n/n;
			else  ans+=1.0*(1<<j)*last1*2.0/n/n;
			if(a[i]&(1<<j))  last1=i;
		}
	}
	return ans;
}
int main(){
	double temp=0;
	scanf("%d",&n);
	for(int i=1;i<=n;++i)  scanf("%d",&a[i]),temp+=1.0*a[i]/n/n;
	printf("%.3lf %.3lf %.3lf",temp+Xor(),temp+And(),temp+Or());
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值