专栏文章
题解:P10500 Rainbow的信号
P10500题解参与者 1已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @mio7rypl
- 此快照首次捕获于
- 2025/12/02 14:45 3 个月前
- 此快照最后确认于
- 2025/12/02 14:45 3 个月前
来源:《算法竞赛进阶指南》,李煜东著,。
原题:。
蒟蒻刚开始做的时候没看懂题解和小蓝皮。后来自己琢磨了一会自己就想通了(其实可能是书里的表述没太看懂),所以现在写一篇比较浅显的题解。
由于这可以算是数学期望的模板题,所以首先对于数学期望,我们来做一些解释。先来看前置知识。
- 随机事件 :可能有多种结果的事件。
- 样本点:事件 的某种可能结果。
- 样本空间 :指某随机事件 的所有可能达成的状态(样本点)组成的集合。
- 概率 :指事件 发生的结果个数占总结果个数(即样本空间)的比重。
- 随机变量 :将样本点映射为实数的函数。我们讨论取值有限的离散型随机变量。
有了这些知识,我们来了解数学期望的定义。
若随机变量 的取值有 ,一个随机事件可表示成 ,其概率为 ,则称 为随机变量 的数学期望。摘自《算法竞赛进阶指南》,李煜东著。
说白了,数学期望就是带权求和,只是权值变成了事件发生的概率,这需要你自己求;而随机变量通常题目里面会给你求法或者定义。
譬如:本题当中给定了你随机变量 的求法,即任意选定的区间 中所有的 的按位 和,而概率则需自己计算。
首先计算概率。根据题意,显然 时会有一个 的对称情况, 。同理 时 。
现在的问题就是处理区间内的位运算。显然采用前缀异或/与/或数组会达到 的时间复杂度,会爆。考虑到位运算不进位,我们可以将整数转换为二进制,按位进行计算,最后合并每一位的贡献即可。
有了这个思路,接下来就是分类讨论了。(然后挂了我很久)
首先我们设目前枚举到了 的第 个二进制数位(从 开始)为右边界,目前称之为 (因为我们之后还要更新)。我们先对朴素的按位与和按位或进行讨论。
记该位之前数字 最后一次出现过的位置为 ,那么我们稍加思索可以推出以下结论:
- 按位或:当 时,对于任意的 ,区间 内的 和的结果一定为 。这是因为按位或“有 全 ”的性质(即 )。此时将 累加到 的答案中。
- 按位与:当 时,对于任意的 ,区间 内的 和的结果一定为 。这是因为按位与“有 全 ”的性质(即 )。此时对 的答案什么都不做。
- 当 时,将 累加到 的答案中(如果出现过 的话)。
- 当 时,将 累加到 的答案中(如果出现过 的话)。
接下来我们开始讨论 的情况。
我们知道 运算和其它运算都不太一样。 运算不仅仅和当前数位有关,它和前面的所有数位 的结果也有关系。所以我们可以开两个变量 、 来记录所有的 的个数,分别满足 和 。那么当 时,将 累加到答案中,反之将 累加到答案中,并需要将 和 进行交换(因为原来 的 结果为 , 运算过后结果变成了 ,而原来 对应的区间 的结果就变成了 )。注意我们需要在交换之前 来更新之后的 。
代码其实写出来也差不多,但还是粘上来吧。
CPP#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);
}
一些提醒
- 注意浮点数类型不要开 。因为你会发现你的程序除了 什么都不输出。
是的我第一遍就挂在这里 - 涉及乘法,记得开 。否则你会由 ,这很不好笑。实例。
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...