P5131 荷取融合 —— DP

题意

有 $ n $ 个正整数构成的序列 $ a_i $,进行如下 $ k $ 次操作:

  • $ pos \gets x $ ( $ x \in [pos,n] $ ) 。 然后将 $ pos $ 加入 $ A $ ( $ pos $ 和 $ x $ 均为为正整数且 $ pos $ 初始为1,$ A $ 初始为空集)。

令 $ ans = \sum _{j \in A} a_j $,求 $ ans $ 的期望。

分析

观察数据范围:$ n \le 10^5 , k \le 300 $。

结合时限1s,我们只能接受时间复杂度为 $ O(n \ast k) $ 的算法。

考虑状态设计: $ f_{i,j} $ , $ g_{i,j} $ 表示在 $ j $ 次操作后 $ pos =i $ 时的权值积之和与操作方案数。

那么 $ f_{i,j} $ 由 $ f_{i,j-1} $ 转移而来:

\[f_{i,j} = \sum_{w=1}^{i} f_{w,j-1} \ast a_w \]

$ g_{i,j} $ 由 $ g_{i,j-1} $ 转移而来:

\[g_{i,j}= \sum_{w=1}^{i} g_{w,j-1} \]

记录 $ f_{i,j-1} $ 和 $ g_{i,j-1} $ 的前缀和,在处理到 $ f_{i,j} $ , $ g_{i,j} $ 时就可以直接计算。

再观察空间限制:125MB。

因为当前状态只由上一次选择转移而来,所以用滚动数组优化。

令 $ ans1 = \sum_{i=1}^n f_{i,k} $ , $ ans2 = \sum_{i=1}^n g_{i,k} $ , \(ans = \frac{ans1}{ans2} \text{(mod 19260817)}\)

注意取模,求逆元即可。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+100,K=3e2+10;
const ll mod=19260817;
int n,k;
ll f[N][2],g[N][2],h[N];
ll qc(ll a, ll b)
{
    ll sum=1;
    while(b)
    {
        if(b&1)
            sum=sum*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return sum;
}
ll ny(ll x)
{
    return qc(x,mod-2ll);
}
int main()
{

    cin>>n>>k;
    for(int i=1;i<=n;++i)
    {
        scanf("%lld",h+i);
        g[i][1]=1;
        f[i][1]=h[i];
    }
    int opt=1;
    for(int i=2;i<=k;++i)
    {
        ll sum=0,num=0;
        opt^=1;
        for(int j=1;j<=n;++j)
        {
            sum=(sum+f[j][1-opt])%mod;
            num=(num+g[j][1-opt])%mod;
            f[j][opt]=sum*h[j]%mod;
            g[j][opt]=num%mod;
        }
    }
    ll ans1=0,ans2=0;
    for(int i=1;i<=n;++i)
        ans1=(ans1 + f[i][opt])%mod;
    for(int i=1;i<=n;++i)
        ans2=(ans2 + g[i][opt])%mod;
    printf("%lld",ans1*ny(ans2)%mod);
    return 0;
}
posted @ 2025-01-16 21:32  Glowingfire  阅读(22)  评论(0)    收藏  举报