1 条题解
-
0
自动搬运
来自洛谷,原作者为

wlj_55
**搬运于
2025-08-24 21:53:51,当前版本为作者最后更新于2019-10-16 12:05:51,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题意简述
有两个数,其中是随机生成的间一个正整数,有的概率为间一个数,使得最大,有的概率生成方式同,求的期望值。
解题思路
我们先考虑暴力的解法。
35pts
,随便怎么暴力都可以过。
当时,即求
$$\frac{\sum_{i=1}^n\sum_{j=1}^n i\space xor\space j}{n^2} $$直接两重循环求解即可,时间复杂度,记得开
当时,我们只需对上述情况的第二层循环做出微调,将求和改为求最大值,再讲每一个所对应的最大值分别相加即可。
代码略。
50pts
我们可以观察到后面的数据是的形式,那么它们间一定有着一些特殊规律。
经
打表检验得:当时,答案为
当时,答案为
下面给出证明:
首先,在时,对于每个数,总有一个数使得,所以期望为
在时,我们首先有一个结论:
$$x\space xor\space i+x\space xor\space (2^k-i-1)=2^k-1\qquad i∈[0,2^k) $$证明也很简单,
手玩一下就好了。所以我们对间数两两配对,求得期望为$\frac{n\times \frac{n\times(n-1)}{2}}{n^2}=\frac{n-1}{2}$
100pts
由于这里有的情况,所以我们要先解决这种情况。
设为总的期望值,为取时的期望,为取时的期望,
由期望的一些基本知识可以很容易的推出
那么下面就是如何计算的问题了。
先讨论时的情况,
我们设对于两个数异或起来的值,第位为为事件,第位为为事件,由位运算的性质知相互独立,故我们可以分开计算。
我们再设从中选出一个数,其二进制第位为的概率为,那么刚才的答案就是
$$\sum_{i=0}^{\log n}2\times p_i\times (1-p_i)\times2^i $$考虑对于区间,一定有区间的所有数的第位均为,区间的所有数第位均为。
然后我们考虑区间,那么必定有区间中的数第位为0,区间中数的第位为0,所以第位为1的数的个数是:
$$\lfloor\frac{n}{2^{k+1}}\rfloor\times 2^k+max(n\space mod\space2^{k+1}-2^k,0) $$故概率为
$$\frac{\lfloor\frac{n}{2^{k+1}}\rfloor\times 2^k+max(n\space mod\space2^{k+1}-2^k,0)}{n} $$时间复杂度
我们再来考虑时的情况(
比较毒瘤)我们设为内使最大的的值
如果没有范围的限制的话,应为按位取反后的值,现在多了一个的限制,那我们可以考虑用一种贪心的手法保留高位的,如果某一位取会使,那么这一位就只能取。
我们考虑最高的位和的前位相同的所有的对答案的贡献,我们考虑与的第位,有下列情况:
的第位为,由于,所以的第位和的第位必须为。
的第位为,那么第位的取值又可以分两种情况:
①的第位为,那么的第位为,且以后的位数一定可以取取反后的值。
②的第位为,那么的第位为,但还有后面的限制。
每次处理时的规模将减半,故时间复杂度为
Code
#include<bits/stdc++.h> #define ll long long using namespace std; double solve1(ll n){ double ret=0; ll Pow=0,tmp=n-1; while(tmp!=0){ Pow++; tmp>>=1; } for(int i=Pow;i>0;i--){ ll nw=(n>>i)*(1LL<<i-1)+min(n-(n>>i<<i),1LL<<i-1); double p=double(n-nw)/n; ret+=(1.0-p)*(1LL<<(i-1))*p; } return ret*2.0; } double solve2(ll n){ if(n==1) return 0.0; double ret=0.0; ll v=1LL,delta,num,tmp=n-1; n--; while(v<=tmp){ v<<=1; } delta=v-1LL; v>>=1; ret+=(double)delta*(n-v+1); ret+=(double)v*v; num=v,delta>>=1; while(v!=1){ v>>=1,delta>>=1; if(n&v){ ret+=(double)num*v; ret+=(double)(num>>1)*delta; num>>=1; } else ret+=(double)(num>>1)*v; } return ret/(double)(n+1); } int main(){ ll n; double p; scanf("%lld%lf",&n,&p); double p1=solve1(n),p2=solve2(n),ans=(1.0-p)*p1+p*p2; printf("%.6lf\n",ans); }如果大家有不懂的可以问我,我会尽我所能解答。
- 1
信息
- ID
- 2855
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者