题目背景
2016.05.19 T1
题目描述
记者弄了个大新闻,这个新闻是一个在 [0,n) 内等概率随机选择的整数,记其为 x 。为了尽可能消除这个大新闻对公众造成的不良印象,我们需要在 [0,n) 内找到某一个整数 y ,使得 x⊕y 达到最大值。这里 ⊕ 代表异或。
问题在于,记者有可能对大新闻进行了加密。情报显示,大新闻没有被加密的概率为 p 。我们决定采取这样的策略:如果大新闻没有被加密,那么我们选出使得 x⊕y 最大的 y ;否则,我们在 [0,n) 内等概率随机选择一个整数作为 y 。
请求出 x⊕y 的期望值。
输入格式
输入仅包含一行,其中有一个正整数 n 和一个实数 p ,含义如问题描述中所述。p 至多精确到小数点后六位。
输出格式
输出一行,代表 x⊕y 的期望值。只有当你的输出与标准输出的相对误差不超过 10^5 时,你的输出才会被判为正确。建议保留至少六位小数。
样例数据1
输入
3 0.5
输出
2.000000
样例数据2
输入
123456 0.5
输出
98063.674346
备注
【样例解释】
考虑样例一。如果大新闻没有被加密,那么可能的 x 与对应的 y 的取值如下:
此时的期望值为 8/3。
如果大新闻被加密了,那么可能的 x 和 y 的取值如下:
此时的期望值为 12/9 = 4/3。
所以总的期望值为 2 。
【数据规模与约定】
所有测试点的数据规模如下:
对于全部测试数据,1≤n≤10^18。
分析: 50%:对于前十个点p=0或1十分有特殊性,n<=100时,不妨直接暴力;n=2^k时,若p=0,期望为(2^k-1)/2,若p=1,期望为2^k-1,这两个是很好推的;至于后面的大数据,不妨做一名mo法师[滑稽。
100%:暂时看着吧……(假装看懂了的样子)
代码:
50%(本人考场作)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;
int getint()
{
int sum=0,f=1;
char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;ch>='0'&&ch<='9';ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
long long getlong()
{
long long sum=0,f=1;
char ch;
for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
if(ch=='-')
{
f=-1;
ch=getchar();
}
for(;ch>='0'&&ch<='9';ch=getchar())
sum=(sum<<3)+(sum<<1)+ch-48;
return sum*f;
}
const double eps=0.000000001;
int mx[110];
bool bj;
long long n,ans1,mi[65];
double ans;
double p;
int main()
{
freopen("news.in","r",stdin);
freopen("news.out","w",stdout);
n=getlong();p=getint();
mi[0]=1;
if(n==mi[0])
{
bj=1;
ans1=mi[0];
}
for(int i=1;i<=60;++i)
{
mi[i]=(mi[i-1]<<1);
if(n==mi[i])
{
bj=1;
ans1=mi[i];
}
}
if(fabs(p-1)<eps)
{
if(bj==1)
{
printf("%I64d.000000\n",ans1-1);
return 0;
}
else
if(n<=100)
{
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
if((i^j)>mx[i])
mx[i]=(i^j);
for(int i=0;i<n;++i)
ans+=mx[i];
ans=ans*1.0/n;
printf("%0.6f\n",ans);
return 0;
}
else//n<=10^18
{
printf("19260817.192608\n");//-1s[滑稽
return 0;
}
}
else
if(fabs(p-0)<eps)
{
if(n<=100)
{
for(int i=0;i<n;++i)
for(int j=0;j<n;++j)
ans+=(i^j);
ans=ans*1.0/(n*n);
printf("%0.6f\n",ans);
return 0;
}
else
if(bj==1)
{
ans1=(n-1)/2;
printf("%I64d.500000\n",ans1);
return 0;
}
else//n<=10^18
{
printf("19260817.192608\n");//-2s[滑稽
return 0;
}
}
else//0<=p<=1
{
printf("19260817.192608\n");//-3s[滑稽
return 0;
}
return 0;
}
100%(膜hyj大佬orz,就他一个AC了),暂时看着吧……(继续假装看懂了的样子)
#include <bits/stdc++.h>
using namespace std;
long long n;
double p;
double ans1, ans2, pi[65], ans;
long long pow1[65];
int bit[65], cnt;
long double f[65][2][2], g[65][2][2];
int main () {
cin >> n;
cin >> p;
pow1[0] = 1;
for (int i = 1; i <= 62; ++i) {
pow1[i] = pow1[i - 1] * 2;
}
int up = log2(n);
cout << setiosflags(ios::fixed) << setprecision(6);
// cout<<"up = "<<up<<'\n';
for (long long i = 0; i <= up; ++i) {
pi[i] = ((double)(n / pow1[i + 1]) * pow1[i] + (double)max(n % pow1[i + 1] - pow1[i], 0ll)) / n;
// printf("pi[%d] = ",i);
// cout<< pi[i]<<'\n';
}
for (int i = 0; i <= up; ++i) {
ans1 += 2 * pi[i] * (1 - pi[i]) * pow1[i];
}
long long t = n - 1;
for (; t; t >>= 1)bit[++cnt] = t & 1;
f[cnt][0][1] = f[cnt][1][0] = pow1[cnt - 1];
g[cnt][0][1] = g[cnt][1][0] = 1;
int nxtj, nxtk;
long long nxtans;
for (int i = cnt ; i ; --i) {
for (int j = 0 ; j < 2; ++j) {
for (int k = 0; k < 2; ++k) {
if (g[i][j][k])
for (int x = 0; x < 2; ++x) {
if (j && (x > bit[i - 1])) continue;
nxtj = 1, nxtk = 1;
nxtans = 0;
if (!j || x < bit[i - 1])nxtj = 0;
if (!k || !x < bit[i - 1]) nxtk = 0;
if (!k || !x <= bit[i - 1]) nxtans = pow1[i - 2] ; else nxtans = 0;
f[i - 1][nxtj][nxtk] += f[i][j][k] + g[i][j][k] * nxtans;
g[i - 1][nxtj][nxtk] += g[i][j][k] ;
}
}
}
}
for (int i = 0 ; i < 2; ++i) {
for (int j = 0; j < 2; ++j) {
// printf("f[%d][%d][%d] = %lf\n", k, i, j, (double)f[k][i][j]);
ans2 += (long double)f[1][i][j] * p / (long double)n;
}
}
ans += ans1 * (1 - p) + ans2;
cout << ans << '\n';
}
本题结?No!No!No!没弄懂怎么能叫结呢?