T1 [湖南集训]大新闻
[湖南集训]大新闻
题目描述
记者弄了个大新闻,这个新闻是一个在
[
0
,
n
)
[0,n)
[0,n) 内等概率随机选择的整数,记其为
x
x
x 。为了尽可能消除这个大新闻对公众造成的不良印象,我们需要在
[
0
,
n
)
[0,n)
[0,n) 内找到某一个整数
y
y
y ,使得
x
⊕
y
x⊕y
x⊕y 达到最大值。这里
⊕
⊕
⊕ 代表异或。
问题在于,记者有可能对大新闻进行了加密。情报显示,大新闻没有被加密的概率为 p 。我们决定采取这样的策略:如果大新闻没有被加密,那么我们选出使得
x
⊕
y
x⊕y
x⊕y 最大的
y
y
y ;否则,我们在
[
0
,
n
)
[0,n)
[0,n) 内等概率随机选择一个整数作为
y
y
y 。
请求出
x
⊕
y
x⊕y
x⊕y 的期望值。
输入格式
输入仅包含一行,其中有一个正整数
n
n
n 和一个实数
p
p
p ,含义如问题描述中所述。
p
p
p 至多精确到小数点后六位。
输出格式
输出一行,代表
x
⊕
y
x⊕y
x⊕y 的期望值。只有当你的输出与标准输出的相对误差不超过
1
0
5
10^5
105 时,你的输出才会被判为正确。建议保留至少六位小数。
样例数据 1
输入
3 0.5
输出
2.000000
样例数据 2
输入
123456 0.5
输出
98063.674346
备注
【样例解释】
考虑样例一。如果大新闻没有被加密,那么可能的
x
x
x 与对应的
y
y
y 的取值如下:
此时的期望值为
8
/
3
8/3
8/3。
如果大新闻被加密了,那么可能的
x
x
x 和
y
y
y 的取值如下:
此时的期望值为
12
/
9
=
4
/
3
12/9 = 4/3
12/9=4/3。
所以总的期望值为
2
2
2 。
【数据规模与约定】
所有测试点的数据规模如下:
对于全部测试数据,
1
≤
n
≤
1
0
1
8
1≤n≤10^18
1≤n≤1018。
思路:
把问题拆成两个子问题,对于加密和没加密分别求解,最后
a
n
s
=
p
×
a
n
s
p
=
0
+
(
1
−
p
)
×
a
n
s
p
=
1
ans=p\times ans_{p=0}+(1-p)\times ans_{p=1}
ans=p×ansp=0+(1−p)×ansp=1。
考虑加密:
把
[
0
,
n
)
[0,n)
[0,n)中任意两数异或求和。
对于
[
0
,
n
)
[0,n)
[0,n)中的数,统计每一位上
0
0
0和
1
1
1出现的次数。对于一位上,
0
0
0或
1
1
1与自己异或对答案没有贡献。最高位为第
n
n
n位,则统计答案为
a
n
s
=
∑
i
=
1
n
n
u
m
[
i
]
[
0
]
×
n
u
m
[
i
]
[
1
]
×
2
i
−
1
ans=\sum_{i=1}^{n} num[i][0]\times num[i][1]\times 2^{i-1}
ans=∑i=1nnum[i][0]×num[i][1]×2i−1
考虑不加密:
数位dp
我也不知道怎么搞,没写出来,先咕着
咕~~~
代码:
#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int ull
typedef unsigned long long ull;
inline char ch(){
static char buf[1<<21],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
}
inline int in{
int s=0,f=1;char x;
for(x=getchar();x<'0'||x>'9';x=getchar()) if(x=='-') f=-1;
for( ;x>='0'&&x<='9';x=getchar()) s=(s<<1)+(s<<3)+(x&15);
return f==1?s:-s;
}
int n;
double p;
double ans1=0,ans2=0;
int sum[100][2];
inline void DP1(){
if(n==1){
ans1=0;
return;
}
int k=1;
while(k<n) k<<=1;
int num=k-1;
k>>=1;
int cnt=k;
ans1=1.0*num*(n-k)+1.0*k*k;
num>>=1;
while(k!=1){
k>>=1;
num>>=1;
if((n-1)&k){
ans1=ans1+1.0*cnt*k+1.0*(cnt>>1)*num;
cnt>>=1;
}
else ans1=ans1+1.0*(cnt>>1)*k;
}
ans1=ans1/n;
return;
}
inline void DP0(int now,int maxx,int w){
if(!maxx) return;
if(now>=maxx){
sum[w][0]+=maxx;
sum[w][1]+=now-maxx+1;
for(int i=1;i<w;i++){
sum[i][0]+=maxx>>1;
sum[i][1]+=maxx>>1;
}
DP0(now-maxx,maxx>>1,w-1);
}
else{
sum[w][0]+=now+1;
DP0(now,maxx>>1,w-1);
}
return;
}
signed main(){
n=in;
scanf("%lf",&p);
DP1();
int k=1,w=1;
while(k<<1<n)
k<<=1,w++;
DP0(n-1,k,w);
int mul=1;
for(int i=1;i<=w;i++){
ans2=ans2+1.0*sum[i][0]*2/n*sum[i][1]/n*mul;
mul<<=1;
}
double ans=ans1*p+ans2*(1-p);
printf("%0.6lf\n",ans);
return 0;
}