CF1591F Non-equal Neighbours

Non-Intersecting Subpermutations

Description

给定整数 \(n,m\)

定义一个长度为 \(n\) 的数列的价值为可以选出满足以下条件的子区间的最多数量总和:

  • 子区间的每个元素互不相同;
  • 子区间长度为 \(m\)
  • 子区间的每个元素取值范围为 \([1,m]\)

计算数列元素取值范围为 \([1,m]\) 且数列长度为 \(n\) 的所有数列的价值之和,答案对 \(998244353\) 取模。

其中 \(2\le m\le n\le4000\)

Solution

既然要求每个元素互不相同,就不难想到设计状态为 \(f_{i,j}\) 表示长度为 \(i\) 的数列,末尾连续 \(j\) 位互不相同的价值和。

但是这样设计状态的话,会发现不好转移,因为我们并不知道当前取的区间会不会和之前取的区间冲突。因此我们再设计一个状态为 \(g_{i,j}\) 表示长度为 \(i\) 的数列,末尾连续 \(j\) 位互不相同的方案数。

特殊地,我们令 \(f_{i,0}\)\(g_{i,0}\) 表示末尾连续 \(m\) 位互不相同,即满足条件的方案数。在计算时下标第二维取模即可。

容易推出 \(g\) 的转移:

\[g_{i,j}=(m-j+1)g_{i-1,j-1}+\sum_{k=j}^{m-1}g_{i-1,k} \]

前半部分表示在原先的部分上再加一个不同的元素。后半部分表示将原先的部分后面接一个已经出现过的元素。比如当前末尾已有 \(3\) 个互不重复的元素为 1,3,4,这时候加入一个 3,将会导致末尾互不重复元素个数变为 \(2\),即 4,3

接着用 \(g\) 推出 \(f\)

\[f_{i,j}=(m-j+1)f_{i-1,j-1}+[j=0]\cdot g_{i,0}+\sum_{k=j}^{m-1}f_{i-1,k} \]

其中 \([p]\) 表示命题 \(p\) 为真是值为 \(1\),否则值为 \(0\)

前面部分和后面部分与求 \(g\) 同理。中间部分表示当已经凑出符合条件的子区间时的贡献,即方案数。最终答案即为 \(\sum_{i=0}^{m-1} f_{n,i}\)

这样转移是 \(O(nm^2)\) 的,需要进行优化。

可以发现我们在计算时用到的 \(\sum\)\(f/g\) 的后缀和,因此可以优化为 \(O(nm)\)

Talk is cheap, show me the code

写的时候为了方便用了刷表法,有些细节自己转换一下。

/*
author:xh_forever
time:2023.9.1
*/
#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 4010, mod = 998244353;

int n, m, ans, f[N][N], g[N][N];

signed main(){
    scanf("%lld%lld", &n, &m), g[1][1] = m;
    for (int i = 1, sum; i <= n; ++i){
        sum = 0;
        for (int j = m - 1; j; --j) g[i + 1][j] = sum = (sum + g[i][j]) % mod;
        for (int j = 0; j < m; ++j) (g[i + 1][(j + 1) % m] += (m - j) * g[i][j] % mod) %= mod;
    }
    for (int i = 1, sum; i <= n; ++i){
        sum = 0;
        for (int j = m - 1; j; --j) f[i + 1][j] = sum = (sum + f[i][j]) % mod;
        for (int j = 0; j < m; ++j) (f[i + 1][(j + 1) % m] += (m - j) * f[i][j] % mod + (j + 1 == m) * g[i + 1][0]) %= mod;
    }
    for (int i = 0; i < m; ++i) (ans += f[n][i]) %= mod;
    printf("%lld\n", ans);
    return 0;
}
posted @ 2023-09-15 14:39  leoair  阅读(25)  评论(0)    收藏  举报