zoukankan      html  css  js  c++  java
  • 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
    ",g[N][P][K]);
      }
      return 0;
    }
    
    
  • 相关阅读:
    SQL SELECT DISTINCT 语句
    SQL SELECT 语句
    SQL 语法
    Linux 命令大全
    MySQL 安装
    Nginx 安装配置
    linux yum 命令
    Linux 磁盘管理
    Linux 文件与目录管理
    Linux 用户和用户组管理
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6994732.html
Copyright © 2011-2022 走看看