题解 P5131 荷取融合


分析

首先看数据范围就知道这应该是一个 \(O(nk)\) 的题。考虑 dp。

\(f[i][j]\) 表示抓取了 \(i\) 个,最后一个是 \(j\) 的答案之和,\(g[i][j]\) 表示有多少种不同的抓取方案,最后答案就是所有满足要求的 \(f\) 之和除以对应的 \(g\)

转移时,因为你可以从上一个抓取往右边跑,所以你只会从比 \(j\) 小的地方转移到 \(f[i][j]\)\(g[i][j]\)。所以在循环的同时用两个数分别记录 \(f\)\(g\) 的前缀和即可。

代码

#include 
using namespace std;
#define int long long
const int mod=19260817;
inline int qpow(int ds,int zs){
	int x=1;
	while(zs){
		if(zs&1)x=1ll*x*ds%mod;
		ds=1ll*ds*ds%mod;zs>>=1;
	}
	return x;
}
inline void read(int &res){
	res=0;
	int f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
	res*=f;
}
int n,k;
int ti;
int a[100005],f[2][100005],g[2][100005],b[100005],bb[100005];
//只有128MB,所以要滚动数组 
signed main()
{
	f[0][1]=1;
	g[0][1]=1;
	read(n);read(k);
	for(int i=1;i<=n;i++)read(a[i]);
	for(int i=1;i<=k;i++){
		ti^=1;
		memset(f[ti],0,sizeof(f[ti]));
		memset(g[ti],0,sizeof(g[ti]));
		for(int j=1;j<=n;j++){
			b[j]=(b[j-1]+f[ti^1][j])%mod;//其实一个数就行了,不需要开数组 
			bb[j]=(bb[j-1]+g[ti^1][j])%mod;
			f[ti][j]=b[j]*a[j]%mod;
			g[ti][j]=bb[j];
		}
	}
	int sum=0,cnt=0;
	for(int i=1;i<=n;i++){
		sum=(sum+f[ti][i])%mod;
		cnt=(cnt+g[ti][i])%mod;
	}
	cout<<(sum*qpow(cnt,mod-2)%mod);
	return 0;
}