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;
}

posted @ 2021-02-08 23:00  _int_me  阅读(40)  评论(0编辑  收藏  举报