【洛谷】[COCI2008-2009#4] PERIODNI
阿新 • • 發佈:2022-02-07
題目連結:https://www.luogu.com.cn/problem/P6453
因為一行中如果斷開的話就不算在同一行了,所以我們可以一行一行來考慮。
這樣的話每層都會截出來一個最大的矩形,這有點向一個樹形結構,可以用笛卡爾樹(還是有點難想的)
這裡提一下,對於2312131這樣的棋盤
三個1本來應該是同等級別的,但是在樹上卻有父子關係,這不要緊,因為它們之間的高的差為0
記f[i][j]表示以i為代表的區域內放j個棋的方案數,sz[u]表示以u為根的子樹大小
顯然,對於節點u,其矩陣的長為sz[u],寬為h[u]-h[fa[u]]。
所以我們先跑一個樹上揹包,合併所有子樹的情況,再單獨討論該節點的區域內的放置情況。
假設一共放j個棋,在該區域內放k個棋則方案數為\(\dbinom{sz[u]-j+k}{k} \dbinom{h[u]-h[fa[u]]}{k} k!\)
本來矩陣的長為sz[u],但是要在子節點的範圍內先放上j-k個棋,佔用了j-k列,故還有sz[u]-j+k列能用來放
#include<bits/stdc++.h> using namespace std; const int N = 505, M = 1e6 + 5, P = 1e9 + 7; int n, m, top, stk[N], inv[M], invfac[M], fac[M]; int h[N], f[N][N], sz[N], son[N][2]; inline void init(int n){ inv[1] = fac[1] = fac[0] = invfac[1] = invfac[0] = 1; for (int i = 2; i <= n; ++i){ inv[i] = 1ll * (P - P / i) * inv[P % i] % P; invfac[i] = 1ll * invfac[i - 1] * inv[i] % P; fac[i] = 1ll * fac[i - 1] * i % P; } } inline int C(int x, int y){ if (x < 0 || y < 0 || x > y) return 0; return 1ll * fac[y] * invfac[y - x] % P * invfac[x] % P; } void dfs(int u, int fath){ f[u][0] = 1, sz[u] = 1; int hi = h[u] - h[fath]; for (int i = 0; i <= 1; ++i){ int v = son[u][i]; if (!v) continue; dfs(v, u); for (int j = min(sz[u], m); j >= 0; --j) for (int k = 1; k <= min(sz[v], m - j); ++k) f[u][j + k] = (f[u][j + k] + 1ll * f[u][j] * f[v][k]) % P; sz[u] += sz[v]; } for (int i = min(sz[u], m); i >= 1; --i) for (int j = 1; j <= min(i, hi); ++j) f[u][i] = (f[u][i] + 1ll * f[u][i - j] * C(j, sz[u] - i + j) % P * C(j, hi) % P * fac[j]) % P; } int main(){ init(M - 5); scanf("%d%d", &n, &m); for (int i = 1; i <= n; ++i) scanf("%d", h + i); for (int i = 1; i <= n; ++i){ while (top && h[i] < h[stk[top]]) son[i][0] = stk[top--]; if (top) son[stk[top]][1] = i; stk[++top] = i; } dfs(stk[1], 0); printf("%d\n", f[stk[1]][m]); return 0; }