题意
**记者弄了个大新闻,这个新闻是一个在 [0,n) 内等概率随机选择的整数,记其为 x。为了尽可能消除这个大新闻对公众造成的不良印象,我们需要在 [0,n)内找到某一个整数 y,使得 x ⊕ y 达到最大值。这里 ⊕ 代表异或。
问题在于,**记者有可能对大新闻进行了加密。情报显示,大新闻没有被加密的概率为 p。我们决定采取这样的策略:如果大新闻没有被加密,那么我们选出使得 x ⊕ y 最大的 y;否则,我们在 [0,n) 内等概率随机选择一个整数作为 y。
请求出 x ⊕ y 的期望值。
思路
对于两个问题分开求解。
第一个问题求的是
∑ i = 0 n − 1 ∑ j = 0 n − 1 i x o r j \sum_{i=0}^{n-1}\sum_{j=0}^{n-1}i \; xor\; j i=0∑n−1j=0∑n−1ixorj
考虑对于每一位求贡献。某一位的贡献就是该位为 0 的个数和该位为 1 的个数相乘再乘两倍。
第二个问题求的是
∑ i = 0 n − 1 max j = 0 n − 1 i x o r j \sum_{i=0}^{n-1}\max_{j=0}^{n-1}i \; xor\; j i=0∑n−1j=0maxn−1ixorj
可以用数位 DP 求解。对于某一位 k k k,假如 n − 1 n-1 n−1 第 k k k 位是 1 ,那么第 k k k 位是 1 的 i i i 对应的 j j j 在第 k k k 位上就要选 0 ,这个 j j j 就脱离上界限制了,后面的贡献就是 2 k + 1 − 1 2^{k+1}-1 2k+1−1 。否则的话,一直不能脱离限制,就一直算每一位的贡献。
这道题卡精度,说好的相对误差 1 0 − 5 10^{-5} 10−5 被搞成了绝对误差。建议口胡过去就算了。
代码
//bzoj 3652
#include<bits/stdc++.h>
using namespace std;
#define int long long
typedef long long LL;
const int E = 60;
LL n;
double p, ans1, ans2;
void dfs(int now, LL cnt, bool lim){
if (now < 0) return;
if (!lim){
double pp = double(cnt)/double(n);
ans2 += pp*((1LL<<now+1)-1);
return;
}
if ((n-1>>now)&1){
LL cnt0 = (cnt>>now+1<<now)+min(cnt&((1LL<<now+1)-1), (1LL<<now));
LL cnt1 = cnt-cnt0;
double pp = double(cnt)/double(n);
ans2 += pp*(1ll<<now);
dfs(now-1, cnt0, 1);
dfs(now-1, cnt1, 0);
}
else{
LL cnt0 = (cnt>>now+1<<now)+min(cnt&((1LL<<now+1)-1), (1LL<<now));
LL cnt1 = cnt-cnt0;
double pp = double(cnt1)/double(n);
ans2 += pp*(1ll<<now);
dfs(now-1, cnt, 1);
}
}
signed main()
{
scanf("%lld%lf", &n, &p);
ans1 = ans2 = 0;
for (int i = E; i >= 0; -- i){
LL cnt0 = (n>>i+1<<i)+min(n&((1LL<<i+1)-1), (1LL<<i));
LL cnt1 = n-cnt0;
double pp = double(cnt1)/double(n);
ans1 += (1.0-pp)*(1LL<<i)*pp;
}
ans1 *= 2.0;
dfs(E, n, 1);
printf("%.6f\n", ans1*(1.0-p)+ans2*p);
return 0;
}