发布于 

CF1810G The Maximum Prefix 题解

典型的反转 ,整理一下此类题思路。

题意简述

你现在要按照如下方式生成一个长度为 的数组:

  • 对于 ,有 的概率设 ,有 的概率设

对于一个长度为 的数组,计算 。特别地,。此时 数组的最大前缀和 。现在给定 个正整数 。该数组最大前缀和为 时,该数组的分数为

要求对于所有 ,求出生成出的数组的期望分数对 取模的结果。

思路分析

正向考虑困难,因为我们要加入一个新的前缀和,而这个前缀和的值与其前一位前缀和的值相关,所以需要同时存下最大的 和当前的 ,复杂度升天,那么反向考虑。

此时容易发现,我们向一个序列的开头加入一个 ,会将之后的所有前缀和 ,同时再加入一个新的前缀和。这是相对来说容易转移的。因此,对于某一个 ,我们有如下朴素 过程,设 表示数组 值为 时的期望分数:

直接进行这个过程,由于需要枚举 ,复杂度是 的,不足以通过此题。

考虑状态所设两维难以压掉,尝试去除枚举 所带来的复杂度。观察 过程,我们共进行了 ,而每次 都指向同一组终止状态。因此考虑反转整个 过程,我们从终止状态出发,进行反向转移,最终抵达初始状态。

刚才所设的是“已经”,那么我们可以设“还需要”。具体来说,我们设 表示数组 值需要为 的最终数组的期望分数,换句话来说,我们钦定了 值为 表示在这样的限制条件下, 的期望分数。那么我们有 的答案即为 。有转移:

容易发现与之前的转移形式一致。

此时有可能会产生这样的疑惑:如果仅仅钦定了 值为 ,那么有一部分填法中 值为 ,另一部分 值为 ,那么为什么 能够同时对 做等量的贡献?

这时,考虑 定义。我们此时并不知道 的填法,我们只是要求这一数组的 值为 。那么,如果我们在 处填了 ,无论我们要求 值填为 还是 ,都符合我们对 的要求,自然都应该进行转移。

启发

  • 前缀和问题,正难则反。
  • 多起点单终点问题,考虑反转
  • 反转 需要明确状态定义。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int p[50005],h[50005],dp[5005][5005];
int qkp(int x,int k){
int ret=1;
while(k){
if(k&1) ret=ret*x%mod;
k>>=1;
x=x*x%mod;
}
return ret;
}
int ny(int x){return qkp(x,mod-2);}
signed main(){
int t,n,x,y;
cin>>t;
while(t--){
scanf("%lld",&n);
for(int i=0;i<=n+1;i++)
for(int j=0;j<=n+1;j++)
dp[i][j]=0;
for(int i=1;i<=n;i++){
scanf("%lld%lld",&x,&y);
p[i]=x*ny(y)%mod;
}
for(int i=0;i<=n;i++) scanf("%lld",&h[i]),dp[0][i]=h[i];
for(int i=1;i<=n;i++){
for(int j=0;j<=n;j++){
dp[i][j]=(p[i]*dp[i-1][j+1]%mod+(1-p[i]+mod)*dp[i-1][max(j-1,0ll)]%mod)%mod;
}
printf("%lld ",dp[i][0]);
}
puts("");
}
return 0;
}