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;
} 

posted @ 2024-02-28 21:32  rlc202204  阅读(10)  评论(0编辑  收藏  举报