Loading

UOJ#884. 【UR #27】509 号迷宫

有一个显然的 \(\mathcal O(n^2)\) DP。

考虑利用组合数优化,只在满足纵坐标 \(y | p\) 的位置记录状态并转移。

有障碍,需要做容斥。

四种转移:线对线、点对点、线对点、点对线

组合计数算明白了就简单了。

代码

#include <bits/stdc++.h>

using namespace std;

constexpr int N = 254500 + 10, MOD = 509, M = MOD + 10;

int n, a[N], f[M][M], c[M][M], fl[N], fr[N], g[N];

// c[n][m] -> C(n + m, m)

int main() {
    ios_base::sync_with_stdio(0); cin.tie(nullptr), cout.tie(nullptr);
    cin >> n;
    for (int i = 1; i <= n; i++) cin >> a[i];
    if (a[n] == n) {cout << 0; return 0;}
    for (int i = 0; i < MOD; i++) c[i][0] = c[0][i] = 1;
    for (int i = 1; i < MOD; i++) {
        for (int j = 1; j < MOD; j++) c[i][j] = (c[i - 1][j] + c[i][j - 1]) % MOD;
    }
    fill(fl, fl + n + 1, 1);
    for (int l = 0, r = MOD; r <= n; l += MOD, r += MOD) {
        // line to line
        for (int i = MOD; i <= n; i++) fl[i] = (fl[i] + fl[i - MOD]) % MOD;
        for (int i = l + 1; i <= r; i++) {
            // point to point
            for (int j = l + 1; j < i; j++) if (a[j] <= a[i]) {
                g[i] += g[j] * c[i - j][(a[i] - a[j]) % MOD];
            }
            // line to point
            int lim = min(a[i], MOD - (i - l));
            for (int j = 0; j <= lim; j++) g[i] += fl[a[i] - j] * c[i - 1 - l][j];
            g[i] = (MOD - g[i] % MOD);
        }
        // point to line
        for (int i = l + 1; i <= r; i++) {
            int lim = min(n - a[i], MOD - (r - i + 1));
            for (int j = 0; j <= lim; j++) fr[a[i] + j] += g[i] * c[r - i][j];
        }
        for (int i = MOD; i <= n; i++) fr[i] += fr[i - MOD];
        for (int i = 0; i <= n; i++) fl[i] = (fl[i] + fr[i]) % MOD;
        fill(fr, fr + n + 1, 0);
    }
    cout << fl[n] << '\n';
    return 0;
}
posted @ 2024-05-31 13:27  Chy12321  阅读(84)  评论(0编辑  收藏  举报