BZOJ 2699: 更新 (DP)

题目

对于一个数列A[1…N],一种寻找最大值的方法是:依次枚举A[2]到A[N],如果A[i]比当前的A[1]值要大,那么就令A[1]=A[i],最后A[1]为所求最大值。假设所有数都在范围[1, K]内,按上面的步骤执行,有多少个长度N的数列满足A[1]被更新的次数恰好为P呢?
N,P<=150,K<=300

题解

感觉数据组数有点多,其实可以直接预处理出所有答案。

定义 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示长度为 i i i,用 ≤ j \le j j的数,形成的被更新 k k k次的数列方案数。

这里把第一个数出现也看作更新了一次。

分类转移:

  • j j j第一次出现,方案为: f [ i − 1 ] [ j − 1 ] [ k − 1 ] f[i-1][j-1][k-1] f[i1][j1][k1]
  • 1 → j 1\to j 1j中任意值,方案为: j ⋅ ( f [ i − 1 ] [ j ] [ k ] − f [ i − 1 ] [ j − 1 ] [ k ] ) j\cdot(f[i-1][j][k]-f[i-1][j-1][k]) j(f[i1][j][k]f[i1][j1][k]),这里前缀和相减,是需要之前的最大值恰好为 j j j,这样才能不重不漏。
  • 再加上没有用到最大值 j j j的方案: f [ i ] [ j − 1 ] [ k ] f[i][j-1][k] f[i][j1][k],也就是做个前缀和。

CODE

#include <bits/stdc++.h>
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
inline void rd(int &x) {
	x = 0; char ch; while(!isdigit(ch=getc()));
	do x=x*10+ch-'0'; while(isdigit(ch=getc()));
}
const int mod = 1e9 + 7;
const int MAXN = 150;
const int MAXM = 300; 
int n, k, p, f[MAXN+5][MAXM+5][MAXN+5];
int main () {
	for(int i = 0; i <= MAXM; ++i) f[0][i][0] = 1;
	for(int i = 1; i <= MAXN; ++i)
		for(int j = 1; j <= MAXM; ++j)
			for(int k = 1; k <= MAXN; ++k)
				f[i][j][k] = (f[i-1][j-1][k-1] + 1ll*j*(f[i-1][j][k]-f[i-1][j-1][k]+mod)%mod + f[i][j-1][k]) % mod;
	int T; rd(T); while(T--) rd(n), rd(k), rd(p), printf("%d\n", f[n][k][p+1]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值