P6453 [COCI2008-2009#4] PERIODNI
\(\text{Solution}\)
一道经典的笛卡尔树上的\(DP\),对于行的影响,我们建出笛卡尔树,那么一个点所代表的,就是整块的,不用考虑中间是否断开。这样我们就很好\(DP\),设\(f_{u,i}\)表示当前节点为\(u\),有\(i\)列已经被覆盖了,那么显然是一个树形背包,最后乘以自己对答案的贡献即可。
\(\text{Code}\)
#include<cstdio>
#include<algorithm>
#define LL long long
using namespace std;
const int N = 505, M = 1e6 + 5, P = 1e9 + 7;
int n, m, a[N], son[N][2], siz[N], fa[N], q[N]; LL f[N][N], fac[M], inv[M];
LL getc(int x, int y){return fac[x] * inv[y] % P * inv[x - y] % P;}
void dfs(int u) {
siz[u] = 1, f[u][0] = 1;
for (int i = 0; i < 2; i++) {
int v = son[u][i]; if (!v) continue;
dfs(v), siz[u] += siz[v];
for (int j = min(siz[u], m); j >= 0; j--)
for (int k = 1; k <= min(j, siz[v]); k++)
(f[u][j] += f[u][j - k] * f[v][k] % P) %= P;
}
for (int i = min(siz[u], m); i >= 0; i--)
for (int j = 1; j <= min(i, a[u] - a[fa[u]]); j++)
(f[u][i] += f[u][i - j] * getc(siz[u] - i + j, j) % P * getc(a[u] - a[fa[u]], j) % P * fac[j] % P) %= P;
}
int main()
{
scanf("%d%d",&n,&m); int mx = n;
for (int i = 1; i <= n; i++) scanf("%d",&a[i]), mx = max(mx, a[i]);
inv[0] = fac[0] = inv[1] = fac[1] = 1;
for (int i = 2; i <= mx; i++) fac[i] = fac[i - 1] * (LL)i % P, inv[i] = (LL)(P - P / i) * inv[P % i] % P;
for (int i = 1; i <= mx; i++) inv[i] = inv[i] * inv[i - 1] % P;
for (int i = 1, top = 0; i <= n; i++) {
int k = top;
while (top && a[q[top]] > a[i]) top--;
if (top) son[q[top]][1] = i;
if (top < k) son[i][0] = q[top + 1];
q[++top] = i;
}
int rt = q[1];
for (int i = 1; i <= n; i++) fa[son[i][0]] = fa[son[i][1]] = i;
dfs(rt), printf("%lld\n",f[rt][m]);
}

浙公网安备 33010602011771号