CodeForces 1009E Intercity Travelling 概率DP
题意
- 给我们一个长为n的序列,要求我们从头开始向右走n个节点,每个位置都有1 / 2的概率将我们传送回1号点之前,不过我们只需要完成走n步的任务就可以了。求我们走过的元素和 乘以 2的n - 1次方的期望。
思路
-
重点主要是将题意翻译为上面的“传送回一号元素之前”,这样我们就可以从1号位置考虑。
-
我们定义 \(F[i]\) 为“已经走了i步,走完剩下的步数,获得的元素和乘以2的n - i - 1次方的期望”,也就是原问题的一个子问题
-
然后我们可以想到初始状态
\[F[n] = 0, F[n - 1] = a_1
\]
- 然后继续推可知
\[F[n - 2] = a_1 + F[n - 1] + (a_1 + a_2)\\
F[n - 3] = a_1 * 2 + F[n - 2] + (a_1 + a_2) + F[n - 1] + (a_1 + a_2 + a_3)\\
F[n - 4] = a_1 * 4 + F[n - 3] + (a_1 + a_2) * 2 + F[n - 2] + (a_1 + a_2 + a_3) + F[n - 1] + (a_1 + a_2 + a_3 + a_4)
\]
- 然后可以得到递推公式
\[F[i] = \sum_{i + 1}^{n - 1}F[j] + \sum_{j = 1}^{n - i - 1}{2^{n - i - 1 - j} * \sum_{k = 1}^{j}{a_k}} + \sum_{j = 1}^{n - i}{a_j}
\]
-
用三个变量分别维护一下这三个值同时记得取模即可,答案为F[0]。
-
当然这个递推公式并不是试出来的,而是考虑了对于i状态下的所有子问题的结果。
AC代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const long long modd = 998244353;
int n;
long long ssu;
long long su[1000005];
long long ff[1000005];
int main()
{
scanf("%d", &n);
ssu = 0;
su[0] = 0;
for (int i = 1; i <= n; ++i)
{
long long xx;
scanf("%lld", &xx);
su[i] = (su[i - 1] % modd + xx % modd) % modd;
}
ff[n] = 0;
ssu = 0;
long long sf = 0;
for (int i = n - 1; i >= 0; --i)
{
ff[i] = (su[n - i] % modd + ssu % modd + sf % modd) % modd;
ssu = (ssu * 2 % modd + su[n - i] % modd) % modd;
sf = (sf + ff[i]) % modd;;
}
printf("%lld\n", ff[0]);;
return 0;
}