题解:P10500 Rainbow的信号
Usstzqqch_Iroha · · 题解
来源:《算法竞赛进阶指南》,李煜东著,
原题:
蒟蒻刚开始做的时候没看懂题解和小蓝皮。后来自己琢磨了一会自己就想通了(其实可能是书里的表述没太看懂),所以现在写一篇比较浅显的题解。
由于这可以算是数学期望的模板题,所以首先对于数学期望,我们来做一些解释。先来看前置知识。
- 随机事件
A :可能有多种结果的事件。 - 样本点:事件
A 的某种可能结果。 - 样本空间
\Omega :指某随机事件A 的所有可能达成的状态(样本点)组成的集合。 - 概率
P(A) :指事件A 发生的结果个数占总结果个数(即样本空间)的比重。 - 随机变量
X :将样本点映射为实数的函数。我们讨论取值有限的离散型随机变量。
有了这些知识,我们来了解数学期望的定义。
若随机变量
X 的取值有x1,x2... ,一个随机事件可表示成X=X_i ,其概率为P(X=X_i)=p_i ,则称E(x)=\Sigma p_ix_i 为随机变量X 的数学期望。摘自《算法竞赛进阶指南》,李煜东著。
说白了,数学期望就是带权求和,只是权值变成了事件发生的概率,这需要你自己求;而随机变量通常题目里面会给你求法或者定义。
譬如:本题当中给定了你随机变量
首先计算概率。根据题意,显然
现在的问题就是处理区间内的位运算。显然采用前缀异或/与/或数组会达到
有了这个思路,接下来就是分类讨论了。(然后挂了我很久)
首先我们设目前枚举到了
记该位之前数字
- 按位或:当
B_i=1 时,对于任意的l\le r ,区间[l,r] 内的\text{or} 和的结果一定为1 。这是因为按位或“有1 全1 ”的性质(即1\ \text{or}\ x = 1 )。此时将2^{k-1}\times P(A)\times (r-1) 累加到\text{or} 的答案中。 - 按位与:当
B_i=0 时,对于任意的l\le r ,区间[l,r] 内的\text{and} 和的结果一定为0 。这是因为按位与“有0 全0 ”的性质(即0\ \text{and}\ x=0 )。此时对\text{and} 的答案什么都不做。 - 当
B_i=0 时,将2^{k-1}\times P(A)\times last[1] 累加到\text{or} 的答案中(如果出现过1 的话)。 - 当
B_i=1 时,将2^{k-1}\times P(A)\times (k-last[0]-1) 累加到\text{and} 的答案中(如果出现过0 的话)。
接下来我们开始讨论
我们知道
代码其实写出来也差不多,但还是粘上来吧。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 1e5 + 5;
int n;
int N[maxn];
int pre_xor[maxn],pre_and[maxn],pre_or[maxn];
int /*B[maxn],*/last[2];//开一个B变量就够了
double ans_xor,ans_and,ans_or;
int maxk = -1,len;
/*int calc(int x){
int y=x;
len = 0;
while(y){
y>>=1;
len++;
}
return len;
}*/
//试图计算最大位数发现没有必要
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++){
cin>>N[i];
}
/*for(int i=1;i<=n;i++){
maxk = max(maxk,calc(N[i]));
}*/
//调试代码
int c1,c2;
for(int k=0;k<=30;k++){//这里是从第0位开始的
last[0]=0;
last[1]=0;
c1=0;
c2=0;
for(int i=1;i<=n;i++){
int B = (N[i]>>k)&1;
double ret = (double)(1<<k)/(n*n);
if(B==0){
ans_and+=ret+2.0*ret*(i-last[0]-1);
ans_or+=ret+2.0*ret*(i-1);
ans_xor+=ret+2.0*ret*c1;
}
else{
ans_or+=2.0*ret*last[1];
ans_xor+=2.0*ret*c2;
}//按上面的结论模拟
last[B]=i;
c1++;
if(B) swap(c1,c2);
}
}
printf("%.3f %.3f %.3f",ans_xor,ans_and,ans_or);
}
一些提醒
- 注意浮点数类型不要开
\texttt{long double} 。因为你会发现你的程序除了0.000 什么都不输出。是的我第一遍就挂在这里 - 涉及乘法,记得开
\texttt{long long} 。否则你会由40\gets 100 ,这很不好笑。实例。