洛谷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;
}
浙公网安备 33010602011771号