洛谷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;
}