洛谷P6453 [COCI 2008/2009 #4] PERIODNI 题解 笛卡尔树+背包DP

题目链接:https://www.luogu.com.cn/problem/P6453

解题思路:完全参考自 Marser 大佬的博客。

一开始我暴力合并 \(f_{u, i}\),时间复杂度 \(O(n^3)\)(但是常数应该会很大),竟然卡过了,卡过了的代码如下。

代码1(卡过的):

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll mod = 1e9 + 7;
const int maxn = 505, maxm = 1e6;

ll fac[maxm + 5], f[maxn][maxn], tmp[maxn];
int n, K, a[maxn], ls[maxn], rs[maxn], rt, sz[maxn];

void init() {
    fac[0] = 1;
    for (int i = 1; i <= maxm; i++)
        fac[i] = fac[i-1] * i % mod;
}

ll qpow(int a, int b) {
    ll res = 1;
    for (ll t = a; b; b >>= 1, t = t * t % mod) {
        if (b & 1)
            (res *= t) %= mod;
    }
    return res;
}

ll inv(int a) {
    return qpow(a, mod - 2);
}

ll C(int n, int m) {
    if (m > n) return 0;
    return fac[n] * inv(fac[m]) % mod * inv(fac[n-m]) % mod;
}

void dikaer() {
    stack<int> stk;
    for (int i = 1; i <= n; i++) {
        int last = 0;
        while (!stk.empty() && a[stk.top()] > a[i]) {
            last = stk.top();
            stk.pop();
        }
        if (!stk.empty()) rs[stk.top()] = i;
        else rt = i;
        ls[i] = last;
        stk.push(i);
    }
}

void dfs(int u, int p, int l, int r) {
    if (!u) {
        f[u][0] = 1;
        return;
    }
    dfs(ls[u], u, l, u-1);
    dfs(rs[u], u, u+1, r);
    for (int i = 0; i <= u-l; i++) {
        for (int j = 0; j <= r-u && i + j <= K; j++) {
            for (int k = 0; i+j+k <= min(K, r-l+1); k++) {
                ll tmp = C(r-l+1 - i - j, k) * C(a[u]-a[p], k) % mod * fac[k] % mod;
                (f[u][i+j+k] += f[ ls[u] ][i] * f[ rs[u] ][j] % mod * tmp % mod) %= mod;
            }
        }
    }
}

int main() {
    init();
    scanf("%d%d", &n, &K);
    for (int i = 1; i <= n; i++)
        scanf("%d", a+i);
    dikaer();
    dfs(rt, 0, 1, n);
    printf("%lld\n", f[rt][K]);
    return 0;
}

后来按照上方博客上的方式进行优化。

类似背包的思想合并左右子树的贡献,再和父节点合并。时间复杂度 \(O(n k^2)\)。代码如下。

代码2:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll mod = 1e9 + 7;
const int maxn = 505, maxm = 1e6;

ll fac[maxm + 5], f[maxn][maxn], g[maxn];
int n, K, a[maxn], ls[maxn], rs[maxn], rt, sz[maxn];

void init() {
    fac[0] = 1;
    for (int i = 1; i <= maxm; i++)
        fac[i] = fac[i-1] * i % mod;
}

ll qpow(int a, int b) {
    ll res = 1;
    for (ll t = a; b; b >>= 1, t = t * t % mod) {
        if (b & 1)
            (res *= t) %= mod;
    }
    return res;
}

ll inv(int a) {
    return qpow(a, mod - 2);
}

ll C(int n, int m) {
    if (m > n) return 0;
    return fac[n] * inv(fac[m]) % mod * inv(fac[n-m]) % mod;
}

void dikaer() {
    stack<int> stk;
    for (int i = 1; i <= n; i++) {
        int last = 0;
        while (!stk.empty() && a[stk.top()] > a[i]) {
            last = stk.top();
            stk.pop();
        }
        if (!stk.empty()) rs[stk.top()] = i;
        else rt = i;
        ls[i] = last;
        stk.push(i);
    }
}

void dfs(int u, int p, int l, int r) {
    if (!u)
        return;
    dfs(ls[u], u, l, u-1);
    dfs(rs[u], u, u+1, r);

    fill(g, g+r-l+1, 0);
    for (int i = 0; i <= min(K, u-l); i++)
        for (int j = 0; j <= min(K-i, r-u); j++)
            (g[i+j] += f[ ls[u] ][i] * f[ rs[u] ][j]) %= mod;

    for (int i = 0; i <= min(K, r-l); i++) {
        for (int j = 0; j <= min(K-i, r-l+1-i); j++) {
            (f[u][i+j] += g[i] * C(r-l+1-i, j) % mod * C(a[u]-a[p], j) % mod * fac[j] % mod) %= mod;
        }
    }
}

int main() {
    init();
    scanf("%d%d", &n, &K);
    for (int i = 1; i <= n; i++)
        scanf("%d", a+i);
    dikaer();
    f[0][0] = 1;
    dfs(rt, 0, 1, n);
    printf("%lld\n", f[rt][K]);
    return 0;
}
posted @ 2026-09-07 16:21  quanjun  阅读(7)  评论(0)    收藏  举报