The Journey of Geor Autumn 上海ICPC 2020 E dp新姿势

The Journey of Geor Autumn 上海ICPC 2020 E dp新姿势

题目大意:

求大小是 \(n\) 且满足条件的排列有多少,条件是:给定k,对于任意 \(i>k \,\,\&\,\,i<=n\)\(i\) ,存在 \(a_i>min(a_{i-k},...,a_{i-1})\)

题解:

首先先去试着构造这个排列,首先明确1一定在前面k个数中,任意选择一个位置放1,那么2可以在1的左边,此时可以理解为2前面比她小的是0,也可以放在距离1不超过 \(k\) 的右边,假设1和2相差 \(j\) ,那么1和2中间可以填 \(j-1\) 个数,这些数是没有任何限制的,如果3已经被填了,那么继续往后考虑其他的数,以这样的构造思维来定义这个 \(dp\)

先写出复杂度是 \(O(nk)\) 的算法,定义 \(dp[i]\) 表示

  • 表示已经放了前 i 个位置
  • i 这个位置的值小于当前还没有放的所有值
  • 往前推 j 个, i 这个位置的值也是最小的

这样最后答案就是 \(dp[n]\)

给出 \(dp\) 定义之后可以自己写转移方程了

\(dp[i] += dp[i-j]*A(n-i+j-1,j-1)%mod;\)

\(dp[i]\)\(dp[i-j]\) 转移过来,说明 \(dp[i-j]<dp[i]\)

\(A(n-i+j-1,j-1)\) 因为从 \(i-j\)\(i\) 一共有 \(j\) 个数,中间可以填\(j-1\)个数,底数是 \(n-i+j-1\) 表示的是从 \(i-j\)\(n\) 还有 \(n-i+j\) 个数,但是因为 \(i\) 这个位置的值既要小于当前还没有放的所有值,又要是往前推 \(j\) 个的最小值,所以 \(i\) 这个位置的值一定是最小值,所以要去掉这个值,所以只有 \(n-i+j-1\) 这么多个数的选择。

dp[0] = 1;
for(int i=1;i<=n;i++){
    for(int j=1;j<=min(i,k);j++){
        dp[i] += dp[i-j]*A(n-i+j-1,j-1)%mod;
        dp[i] %= mod;
    }
}

这样的算法还是不行,需要优化,优化考虑去拆A这个排列,然后发现:

还要注意一点的是对于每一个 \(i\) 最多只能考虑前面 \(k\) 个数,所以对 \(i>k\)\(dp\) 要删去前面的最后一个值。

#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn = 1e7+10;
typedef long long ll;
const int mod = 998244353;
ll dp[maxn],inv[maxn],fac[maxn];
void init() {
    fac[0] = 1, fac[1] = 1,inv[1] = 1;
    for (int i = 2; i < maxn; i++) {
        fac[i] = fac[i - 1] * i % mod;
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    }
    inv[0] = 1;
    for (int i = 1; i < maxn; i++) {
        inv[i] = inv[i - 1] * inv[i] % mod;
    }
}
/*
 * 最小的数是0,所以在0和1之间夹很多数,所以1不一定在1这个位置,因为dp[2]也可以从dp[0]转移过来
 */
int main(){
    init();
    int n,k;
    scanf("%d%d",&n,&k);
    dp[1] = dp[0] = 1;
    for(int i=2;i<=n;i++){
        dp[i] = dp[i-1]*(n-i+2)%mod;
        if(i>k){
            ll res = fac[n-i+k] * inv[n-i] % mod;
            dp[i] = (dp[i] - res * dp[i - k - 1]%mod + mod)%mod;
        }
    }
    printf("%lld\n",dp[n]);
    return 0;
}
/*
dp[i] = dp[i-1]
      + dp[i-2]*(n-i+1)
      + dp[i-3]*(n-i+1)*(n-i+2)
 dp[1] = dp[0]
 dp[2] = dp[1]
       + dp[0]*(n-1)
 dp[3] = dp[2]
       + dp[1]*(n-2)
       + dp[0]*(n-1)*(n-2)
*/
posted @ 2021-01-25 20:00  EchoZQN  阅读(136)  评论(0编辑  收藏  举报