bzoj2699 更新
题意
对于一个数列A[1..N],一种寻找最大值的方法是:依次枚举A[2]到A[N],如果A[i]比当前的A[1]值要大,那么就令A[1]=A[i],最后A[1]为所求最大值.假设所有数都在范围[1,K]内,按上面的步骤执行,有多少个长度N的数列满足A[1]被更新的次数恰好为P呢?
对100%的数据,数据组数≤ 1000,1 ≤ N ≤ 150,0 ≤ P < N,1 ≤ K ≤ 300.
分析
先考虑单组数据的情况.我们可以想到一个三维的动态规划:
f[i][j][k]表示长度为i,更新次数为j,最大值为k的序列个数.考虑第i个数是否造成一次更新.
那么
f[i][j][k]=f[i-1][j][k]*k+f[i-1][j-1][1]+f[i-1][j-1][2]+...+f[i-1][j-1][k-1]
这个转移方程显然可以前缀和优化做到O(n^3)转移
最后的答案是f[n][P][1]+f[n][P][2]+...+f[n][P][K]
注意对于不同的输入,我们处理的f数组都是相同的.预处理出f和f的前缀和,可以O(n^3)预处理,O(1)查询.
非常好写.
#include<cstdio>
const int mod=1000000007;
int f[155][155][305];
int g[155][155][305];
void init(){
for(int i=1;i<=300;++i){
f[1][0][i]=1;
}
for(int i=1;i<=300;++i){
g[1][0][i]=g[1][0][i-1]+f[1][0][i];
}
for(int i=2;i<=150;++i){
for(int j=0;j<=i;++j){
for(int k=1;k<=300;++k){
f[i][j][k]=((j?g[i-1][j-1][k-1]:0)+f[i-1][j][k]*1ll*k%mod)%mod;
}
for(int k=1;k<=300;++k){
g[i][j][k]=(g[i][j][k-1]+f[i][j][k])%mod;
}
}
}
}
int main(){
init();
int tests;scanf("%d",&tests);
while(tests--){
int N,K,P;scanf("%d%d%d",&N,&K,&P);
printf("%d\n",g[N][P][K]);
}
return 0;
}