「Codeforces 1189F」Array Beauty

题目链接:Codeforces 1189F

我们定义一个序列 $b_{1}, b_{2}, \dots, b_{n} (n > 1)$ 的「美丽值」为 $\min_{1 \le i < j \le n} \lvert b_{i} - b_{j} \rvert$。

我们给定一个序列 $a_{1}, a_{2}, \dots, a_{n}$ 个一个数字 $k$。请计算出所有长度恰好为 $k$ 的子序列的「美丽值」之和,答案对 $998244353$ 取模。

数据范围:$2 \le k \le n \le 1000$,$0 \le a_{i} \le 10 ^ {5}$。


Solution

首先把 $a$ 数组排序,显然不会影响答案。

直接计算看起来很困难?我们改成枚举「美丽值」统计其数量。貌似还是没法计数?那我们再放松一下条件:统计「美丽值」不小于某个数 $x$ 的子序列数量。

这样一来我们就可以计数了。设计状态 $f(i, j)$ 表示当前子序列长度为 $i$,子序列以第 $j$ 个数结尾,「美丽值」不小于 $x$ 的方案数。

转移方程非常简单:$f(i, j) = \sum f(i - 1, k) (a_{j} - a_{k} \ge x)$,可以直接前缀和优化做到 $\mathcal O(nk)$ 的复杂度。

通过鸽巢原理,我们可以得到 $x$ 的最大值为 $\left \lfloor \frac{\max \{ a_{i} \} }{k - 1} \right \rfloor$,于是总复杂度为 $\mathcal O(nk \cdot \frac{\max \{ a_{i} \} }{k - 1}) = \mathcal O(n \cdot \max \{ a_{i} \} )$。

时间复杂度:$\mathcal O(n \cdot \max \{ a_{i} \} )$。


Code

#include <cstdio>
#include <algorithm>

const int N = 1e3 + 5;
const int MOD = 998244353;

int n, k, a[N], p[N], f[N][N], sum[N][N];

void add(int &x, int y) {
    (x += y) >= MOD && (x -= MOD);
}
int solve(int x) {
    for (int i = 0; i <= k; i++) {
        for (int j = 0; j <= n; j++) {
            f[i][j] = sum[i][j] = 0;
        }
    }
    for (int i = 1, j = 0; i <= n; i++) {
        for (; a[i] - a[j + 1] >= x; j++);
        p[i] = j;
    }
    f[0][0] = 1;
    std::fill(sum[0], sum[0] + n + 1, 1);
    for (int i = 1; i <= k; i++) {
        for (int j = i; j <= n; j++) {
            f[i][j] = sum[i - 1][p[j]];
            sum[i][j] = f[i][j];
        }
        for (int j = i; j <= n; j++) {
            add(sum[i][j], sum[i][j - 1]);
        }
    }
    return sum[k][n];
}
int main() {
    scanf("%d%d", &n, &k);
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    std::sort(a + 1, a + n + 1);
    int ans = 0;
    for (int t = a[n], i = 1; i * (k - 1) <= t; i++) add(ans, solve(i));
    printf("%d\n", ans);
    return 0;
}

发表评论