洛谷P5131 荷取融合

题目

https://www.luogu.com.cn/problem/P5131

思路

刚开始看到这道题以为是一个经典的移项求DP方程的概率DP题,也就是说每一步过程都是随机转移,后来发现理解错了题意。。。

观察一下样例珂以发现,题目的意思实际是说:每一个合法的抓取序列出现的概率是相同的。所以我们只要求出所有合法序列的总贡献除以合法序列的总数即可。

我们设开头为\(i\),长度为\(j\)的序列的总贡献为\(f(i,j)\),则有$$f(i,j)=a[i]*\sum_{k=i}^n f(k,j-1)$$

注意到这个递推式只和上一层的状态有关,所以我们珂以像背包问题一样采取合适的枚举顺序优化一维空间。

这个\(\sum\) 其实就是一个后缀和,不影响复杂度。

那么接下来就是求合法序列的总数(假定为 \(m\) ),注意到机械臂只能不动或向右,相当于固定顺序,所以是一个组合问题,又因为珂以不动,所以是一个可重集组合问题。

\(m=\tbinom{n+k-1}{n-1}\)(这个模型很经典,珂以构造母函数也珂以利用组合意义隔板法解决)。

最终答案是$$m^{-1} \sum_{i=1}^n f(i,k)$$

算法时间复杂度\(\Theta(nk)\),空间复杂度\(\Theta(n+k)\)

代码

点击查看代码
#include<cstdio>
#include<cstdlib>
#define mod 19260817
#define maxn 120000
#define ll long long
using namespace std;
ll a[maxn];
ll dp[maxn],fac[maxn];
ll qpow(ll x,int p){
    ll ans=1,base=x;
    for(;p;p>>=1){
        if(p&1) ans=ans*base%mod;
        base=base*base%mod;
    }
    return ans;
}
ll inv(ll x){
    return qpow(x,mod-2);
}
int main(){
    int n,k,i,j;
    ll ans=0,m=0;
    scanf("%d%d",&n,&k);
    for(i=1;i<=n;++i) scanf("%lld",&a[i]);
    fac[0]=1;
    for(i=1;i<=n+k;++i) fac[i]=fac[i-1]*i%mod;
    for(i=1;i<=n;++i) dp[i]=a[i];
    for(i=2;i<=k;++i){
        ll sum=0;
        for(j=n;j>=1;--j){
            sum=(sum+dp[j])%mod;
            dp[j]=sum*a[j]%mod;
        }
    }
    for(i=1;i<=n;++i) ans=(ans+dp[i])%mod;
    m=(fac[n+k-1]*inv(fac[k])%mod)*inv(fac[n-1])%mod;
    printf("%lld",ans*inv(m)%mod);
    // system("pause");
    return 0;
}
posted @ 2022-03-14 23:41  文艺平衡树  阅读(21)  评论(0编辑  收藏  举报