2019杭电多校第7场 K Kejin Player HDU 6656(数学推导)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6656

 

题目大意:对于每一个等级,可以花ai元,有pi概率升级,如果升级失败就退到xi级,问从li级升到ri级的钱数期望

 

题目思路:比赛的时候已经想到思路了,结果由于推导太慌了出了很多小毛病,被队友先A了,赛后重新推导了一遍就出来了。

首先定义dp[i]为从1出发以后第一次到i的期望。

那么dp[i]升级到下一关要么是一次性成功,要么是失败一次以后成功,要么是失败两次后成功....依次类推。

由于当到达一个点x时,你接下来要发生的事情与之前做的事情没有任何关联,无论你用何种方式到达x,你接下来的待遇都会被一视同仁,也就是我当前的决策对于将来没啥影响。所以可以得到一个很重要的结论,那就是l到r的期望金钱就是1到r的期望金钱-1~l的期望金钱,因为到r需要的钱数-到l需要的钱数就是之间的钱数。然后我们就可以列出下列方程。以第x次成功升级为例,先花dp[i]到达i点,然后因为失败了x-1次,所以就需要(x-1)*a[i]元,然后每次失败你都得费劲千辛万苦回到i进行下一次冲刺,回来需要花dp[i]-dp[x]元,一共需要回来x-1次,再加上第x次成功的a[i]元,也就是(dp[i]+x*a[i]+(x-1)*(dp[i]-dp[x_{i}])*p[i]*(1-p[i])^{x-1}

 

所以说列DP方程如下:

dp[i]= (dp[i]+a[i])*p[i]+ (dp[i]+2*a[i]+dp[i]-dp[x_{i}])*p[i]*(1-p[i])+ (dp[i]+3*a[i]+2*(dp[i]-dp[x_{i}]))*p[i]*(1-p[i])^{2}+...+ (dp[i]+x*a[i]+(x-1)*(dp[i]-dp[x_{i}])*p[i]*(1-p[i])^{x-1}

然后对于dp[i],a[i],dp[i]-dp[x_{i}]分别单拉出来化简,dp[i]的项直接用等比数列求和公式即可,其他两项都可以通过错位相减法很轻松(并不)地得到。化简就是繁琐点难度不大,实在有问题可以评论区问问菜鸡博主。

最后得到的式子是dp[i+1]=dp[i]+\frac{a[i]}{p[i]}+\frac{(dp[i]-dp[x_{i}])*(1-p[i])}{p[i]}

 

以下是代码:

#include

using namespace std;
#define inf 0x3f3f3f3f
#define rep(i,a,b) for(ll i=a;i<=b;i++)
#define per(i,a,b) for(ll i=a;i>=b;i--)
#define ll long long
const ll MAXN = 5e5+5;
const ll MOD = 1e9+7;
ll dp[MAXN];
ll powmod(ll x,ll y){
    ll rst=1;
    for(;y;y>>=1){
        if(y&1)rst=rst*x%MOD;
        x=x*x%MOD;
    }
    return rst;
}
ll r,s,x,a;
ll n,q,c,d;
int main()
{
    ll t;
    scanf("%lld",&t);
    while(t--)
    {
        memset(dp,0,sizeof(dp));
        scanf("%lld%lld",&n,&q);
        rep(i,1,n){
            scanf("%lld%lld%lld%lld",&r,&s,&x,&a);
            ll p=r*powmod(s,MOD-2)%MOD;
            dp[i+1]=(dp[i]+a*s%MOD*powmod(r,MOD-2)%MOD+((dp[i]-dp[x]+MOD)%MOD)*((1-p+MOD)%MOD)%MOD*s%MOD*powmod(r,MOD-2)%MOD)%MOD;
        }
        
        rep(i,1,q){
            scanf("%lld%lld",&c,&d);
            printf("%lld\n",(dp[d]-dp[c]+MOD)%MOD);
        }
    }
    return 0;
}

 

你可能感兴趣的:(思维)