CF441E Valera and Number 题解
题意:
给出一个数 $x$,对它进行 $k$ 次操作,每次操作:
- 以 $p \%$ 的概率对它乘以 $2$。
- 以 $(100 - p) \%$ 的概率对它加上 $1$。
求该数最终二进制下末尾 $0$ 个数的期望。
输入格式:
The first line of the input contains three integers $x,k,p(1\le x\le 10^{9}, 1\le k\le 200, 0\le p\le 100)$ .
输出格式:
Print the required expected value. Your answer will be considered correct if the absolute or relative error doesn’t exceed $10^{-6}$ .
设 $f_{i,j}$ 表示 $x$ 经过执行过 $i$ 次后, $x+j$ 的期望答案,则
最后答案就是 $f_{k,0}$ 。说实话这个转移的前一部分还是有点晕的,我想了好一会。
分析下复杂度。考虑 dp 中一条贡献路径的 $j$ ,显然贡献的过程会使 $j$ 乘 $2$ 或者加 $1$ 。
而当 $j > k$ 时,减 $1$ 操作是不可能使得 $j=0$ 的,因为至多 $k$ 次操作,因此这样的 $j$ 对答案没有贡献
综上,时间复杂度 $\mathcal{O}(k^2)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(555))
int x,k,t;
double p,q,f[N][N];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cout << fixed << setprecision(15);
cin >> x >> k >> t; p = t * 0.01; q = 1.0 - p;
for(int j = 0; j <= k; j++) f[0][j] = (double)(__builtin_ctz(x + j));
for(int i = 1; i <= k; i++)
for(int j = 0; j <= k; j++)
{
f[i][j] += f[i - 1][j + 1] * q;
f[i][j << 1] += (f[i - 1][j] + 1) * p;
}
cout << f[k][0] << '\n';
return 0;
}
参考文献:
[1] https://yhx-12243.github.io/OI-transit/records/cf441E.html
[2] https://www.luogu.com.cn/blog/dowhiletrue/solution-cf441e