题目
对于一个数列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[i−1][j−1][k−1]
- 放 1 → j 1\to j 1→j中任意值,方案为: 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[i−1][j][k]−f[i−1][j−1][k]),这里前缀和相减,是需要之前的最大值恰好为 j j j,这样才能不重不漏。
- 再加上没有用到最大值 j j j的方案: f [ i ] [ j − 1 ] [ k ] f[i][j-1][k] f[i][j−1][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]);
}