QOJ.ac

QOJ

Type: Editorial

Status: Open

Posted by: louyuxuan

Posted at: 2026-05-25 09:18:44

Last updated: 2026-05-25 09:19:26

Back to Problem

New Editorial for Problem #17742

计数题的一个常规思路是考虑这个序列的生成过程。现在我们要生成一个序列,满足所有前缀和不等于任意一个后缀和,我们考虑从两边向中间生成,设当前前缀和为 $pre$,后缀和为 $suf$,若 $pre < suf$,我们可以在前缀填一个不等于 $suf - pre$ 的数,否则我们可以在后缀填一个不等于 $pre - suf$ 的数。初始先在 $pre$ 填一个数。这样的生成方式肯定唯一对应了所有合法的序列。这就相当于一个二维网格,每个点 $(x, y)$ 代表当前前缀为 $x$,后缀为 $y$,从 $(0, n + 1)$ 开始,每次 $x \gets x + 1$ 或 $y \gets y - 1$,第一步一定走到 $(1, n + 1)$,最后一定停在某个 $(i, i + 1)$。如果我们任意走是肯定会算重的,但是我们考虑设定一个规范路径:如果前缀小于后缀就只走 $x \gets x + 1$,否则只走 $y \gets y + 1$,那么一个合法序列就有且只有一种走法。

于是考虑 DP,$f_{i, j, k}$ 表示当前前缀填到 $i$,后缀填到 $j$,$pre - suf = k \in [-V,V]$。转移显然可以前缀和优化做到 $\mathcal{O}(n^2m)$。

namespace Loop1st {
int n, l[N], r[N], f[N][N][(M + 10) << 1], s[N][N][(M + 10) << 1];
int ask(int i, int j, int L, int R) {
    L = max(L, 0);
    R = min(R, M << 1);
    if (L > R) return 0;
    return (s[i][j][R] + mod - (L ? s[i][j][L - 1] : 0)) % mod;
}
void Main(int tc) {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> l[i] >> r[i];
    }
    s[0][n + 1][M] = f[0][n + 1][M] = 1;
    for (int i = M + 1; i <= (M << 1); i++) s[0][n + 1][i] = 1;
    for (int i = 0; i <= n; i++) {
        for (int j = n + 1; j > i; j--) if (i || j <= n) {
            for (int k = -M; k <= M; k++) if (k) {
                if (i) f[i][j][k + M] = ask(i - 1, j, k - r[i] + M, min(M, k - l[i] + M));
                if (j <= n) f[i][j][k + M] = (f[i][j][k + M] + ask(i, j + 1, max(l[j] + k + M, M + 1), r[j] + k + M)) % mod;
            }
            s[i][j][0] = f[i][j][0];
            for (int k = 1; k <= (M << 1); k++) s[i][j][k] = (s[i][j][k - 1] + f[i][j][k]) % mod;
        }
    }
    int ans = 0;
    for (int i = 0; i <= n; i++) for (int j = 0; j <= (M << 1); j++) ans = (ans + f[i][i + 1][j]) % mod;
    cout << ans << '\n';
}

}

Comments

No comments yet.