P6453 题解
题意:
给定一个由 \(n\) 列组成的表格,第 \(i\) 列高 \(a_i\),每一列的底部都是对齐的。
你需要再里面填入 \(k\) 个相同的数。但不得有任意两个数在同一行或者同一列(如果中间隔开则不算)。
请求出填写的方案总数。
\(n, k \le 500, a_i \le 10^6\)。
思路:
神题。
这种形式的表格让人联想到笛卡尔树。
不妨建出笛卡尔树,将表格分成若干个极大的矩形,长宽分别是 \(a_i\) 减去 \(a_{pr}\) 和 \(sz_i\)(子树大小),然后考虑再树上 \(dp\)。
设 \(dp[i][j]\) 表示 \(i\) 子树内选 \(j\) 个,两个儿子的 \(dp\) 值直接合并一下即可。
考虑计算这个点的选取方案。假设 \(x\) 这个点选了 \(i\) 个,子树内选了 \(j\) 个,则有 \(\binom{sz_x - j}{i}\binom{a_x - a_{pr}}{i} i!\),可以类比于棋盘上放车问题。
然后就可以做出这道题了,时间复杂度 \(O(nk^2)\)。
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 505;
const int M = 1e6 + 5;
const int mod = 1e9 + 7;
int fpow(int a, int b, int p) {
if (b == 0)
return 1;
int ans = fpow(a, b / 2, p);
ans = (1ll * ans * ans) % p;
if (b % 2 == 1)
ans = (1ll * a * ans) % p;
return ans;
}
int fac[M] = {0}, inv[M] = {0};
void init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; i++)
fac[i] = 1ll * fac[i - 1] * i % mod;
inv[n] = fpow(fac[n], mod - 2, mod);
for (int i = n - 1; i >= 0; i--)
inv[i] = 1ll * inv[i + 1] * (i + 1) % mod;
}
int cmb(int n, int m) {
if (n < m)
return 0;
return 1ll * fac[n] * inv[m] % mod * inv[n - m] % mod;
}
int n, k, a[N] = {0};
int ls[N] = {0}, rs[N] = {0};
int stk[N] = {0}, tp = 0;
int rt = 0;
void build() {
stk[++tp] = 0;
for (int i = 1; i <= n; i++) {
int pos = tp;
while (a[stk[pos]] > a[i])
pos--;
rs[stk[pos]] = i;
if (pos < tp)
ls[i] = stk[pos + 1];
tp = pos, stk[++tp] = i;
}
rt = rs[0];
}
int dp[N][N] = {{0}};
int dp2[N][N] = {{0}};
int sz[N] = {0};
int dfs(int x, int pr) {
int h = a[x] - a[pr];
// cout << x << " " << pr << endl;
sz[x] = 1;
if (ls[x] != 0)
sz[x] += dfs(ls[x], x);
if (rs[x] != 0)
sz[x] += dfs(rs[x], x);
for (int i = 0; i <= k; i++)
for (int j = 0; i + j <= k; j++)
dp[x][i + j] = (dp[x][i + j] + 1ll * dp[ls[x]][i] * dp[rs[x]][j] % mod) % mod;
/* cout << h << " " << sz[x] << endl;
for (int i = 0; i <= k; i++)
cout << x << " " << i << " " << dp[x][i] << endl;*/
for (int i = 0; i <= k; i++)
for (int j = 0; i + j <= k; j++)
dp2[x][i + j] = (dp2[x][i + j] + 1ll * cmb(h, i) * cmb(sz[x] - j, i) % mod * fac[i] % mod * dp[x][j] % mod) % mod;
for (int i = 0; i <= k; i++)
dp[x][i] = dp2[x][i];//, cout << x << " " << i << " " << dp[x][i] << endl;
return sz[x];
}
int main() {
cin >> n >> k;
for (int i = 1; i <= n; i++)
cin >> a[i];
init(1000000);
build();
dp[0][0] = 1;
dfs(rt, 0);
cout << dp[rt][k] << endl;
return 0;
}