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

posted @ 2017-06-12 19:59  liu_runda  阅读(154)  评论(0编辑  收藏  举报
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难