2
1 2
1 2
2
1 2
1 2
题目大意就是一开始你有0颗星,需要进行对局,每赢一局可以获得一颗星,每输一局会扣除一颗星,给出当你持有\(i\)颗星时的胜率,问获得\(n\)颗星期望对局多少次。
比赛的时候我推了1h的式子,其实已经推出来了,后来像个NT一样在后面加了一点无关的东西结果挂了
这道题目很显然是期望DP,我们设\(f[i]\)表示从\(i-1\)颗星打到\(i\)期望对局的次数,那么转移就很显然了(\(p[i]\)指胜率,即\(x[i]/y[i]\)):
\(f[i]=(1-p[i])*(f[i]+f[i-1])+1\),后面的1表示先进行了一次对局,而有\(1-p[i]\)的几率对局失败,而对局失败后需要从i-2颗星再打到i颗星,即\((1-p[i])*(f[i]+f[i-1])\)
但是我们发现\(f[i]\)是当前需要求的,所以我们考虑转化式子:
\(f[i]=(1-p[i])*(f[i]+f[i-1])+1\)
去括号得:\(f[i]=f[i]+f[i-1]-p[i]*f[i]-p[i]*f[i-1]+1\)
将\(f[i]-p[i]*f[i]\)移至等号左边得:\(p[i]*f[i]=f[i-1]+p[i]*f[i-1]+1\)
乘法分配律得:\(p[i]*f[i]=(1-p[i])*f[i-1]+1\)
两边同处\(p[i]\)得:\(f[i]=((1-p[i])*f[i-1]+1)/p[i]\)
又因为答案是在模意义下的,所以除操作时换成乘逆元就行了
记得初始化:\(f[1]=1/p[1]\)(我的\(p\)从1开始编号)
然后我们就可以愉快地\(O(n)\)转移了
#include<cstdio>
#define N 1000005
#define R register int
#define mo 998244353
#define ll long long
using namespace std;
ll f[N],g[N],p[N],ans;
int n;
inline ll ksm(ll x,ll y)
{
ll res=1;
while (y)
{
if (y&1) res=(res*x)%mo;
x=(x*x)%mo;y>>=1;
}
return res;
}
int main()
{
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%d",&n);
for (R i=1;i<=n;++i)
{
ll x,y;scanf("%lld%lld",&x,&y);
p[i]=x*ksm(y,mo-2)%mo;
}
ans=f[1]=(1*ksm(p[1],mo-2))%mo;
for (R i=2;i<=n;++i)
{
f[i]=(f[i-1]*((1-p[i]+mo)%mo)%mo+1)%mo*ksm(p[i],mo-2)%mo;
ans=(ans+f[i])%mo;
}
printf("%lld\n",ans);
return 0;
}
原文:https://www.cnblogs.com/CMC-YXY/p/14350027.html