来源:第十三届蓝桥杯省赛C++A/研究生组 , 第十三届蓝桥杯省赛Java研究生组 , 第十三届蓝桥杯省赛Python研究生组
这是一个很有意思的概率dp题,最开始,我用传统概率dp去做,从起点推到终点进行决策,发现推导出来的dp递推式如果直接实现的话是得不到正确答案的,因为所有的情况始终会包含E0的数学期望,同时E0的数学期望也是未知,这样就陷入了一个递推循环当中。
看了一下题解,有两种很巧妙的解法,
解法一:是采取我之前说的做法,但是对于dp方程要进行变形从En迭代到E0,然后根据已知条件,我们知道En的值为0,故而就可得到E0的公式方程,题解链接为:AcWing 4646. 爬树的甲壳虫 - AcWing
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
#define PII pair<int,int>
const int N=1e6+10;
const int INF=1e18;
const int mod=998244353;
int x[N],y[N];
int dp[N];
int n;
int q_pow(int a,int b)
{
if(b==0)return 1;
int temp=q_pow(a,b/2);
if(b%2==0)return temp*temp%mod;
else return temp*temp%mod*a%mod;
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>x[i]>>y[i];
int res=1;
int sum=0;
for(int i=n;i>=1;i--)
{
int inv=q_pow(y[i],mod-2);
res=(res*((1-x[i]*inv%mod)%mod+mod)%mod)%mod;
sum=(sum+q_pow(res,mod-2))%mod;
}
cout<<sum<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}
解法二:
利用逆推思想,传统的概率dp惯性思维是从dp[i+1]到dp[i]。但是这样会引入一个E0的未知变量。
因此我们可以重新定义dp的含义,令dp[i]为从树根到高度i所需要的时间的期望值,那么就可以将
递推公式转化为:dp(k)=(dp(k−1)+1)(1−pk)+(dp(k−1)+1+dp(k))pk,,在通过变形,就可以直接用
dp[i-1]来表示dp[i]。从而可以直接求出递推表达式得到答案。
题解链接:AcWing 4646. 爬树的甲壳虫(概率递推+快速幂逆元) - AcWing
代码如下:
#include<bits/stdc++.h>
using namespace std;
#define endl "\n"
#define int long long
#define PII pair<int,int>
const int N=1e6+10;
const int INF=1e18;
const int mod=998244353;
int x[N],y[N];
int dp[N];
int n;
int q_pow(int a,int b)
{
if(b==0)return 1;
int temp=q_pow(a,b/2);
if(b%2==0)return temp*temp%mod;
else return temp*temp%mod*a%mod;
}
int pro(int idx)
{
if(idx==0)return 0;
return y[idx]*(pro(idx-1)+1)%mod*q_pow(y[idx]-x[idx],mod-2)%mod;
}
void solve()
{
cin>>n;
for(int i=1;i<=n;i++)cin>>x[i]>>y[i];
cout<<pro(n)<<endl;
}
signed main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout.tie(nullptr);
int T=1;
// cin>>T;
while(T--)solve();
return 0;
}