bzoj2616. SPOJ PERIODNI(笛卡尔树+dp+组合数学)

题目描述

题解

我们发现矩形是好做的,那么我们就把这个图形划分成若干的矩形。上下不太好搞的,所以我们根据上下来拆矩形。对序列建出笛卡尔树(小根堆),考虑一个点的贡献是其父亲到它的这一段长度内可以放的车,记 (f_{i,j}) 代表在 (i) 的子树内放 (j) 个车的方案数。首先先不考虑 (i) 的贡献,有 (f_{i,j}=sum_{k=0}^{j}f_{lson(i),k} imes f_{rson(i),k})。考虑 (i) 的贡献,即在一个 (n imes m) 的矩形内放 (k) 个点的方案数,容易算出是 ({n choose k} imes {m choose k} imes k!)。那么就简单了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 505;
const ll mod = 1e9 + 7;
struct treap{
	int val, key, sz, son[2], fa;
}tr[MAXN];
int root;
int n, k;
int h[MAXN], stk[MAXN], top;
ll f[MAXN][MAXN], fac[1000005], inv[1000005], ifac[1000005];
ll C(ll i, ll j) {
	if (i < 0 || j < 0 || i < j) return 0;
	return fac[i] * ifac[j] % mod * ifac[i - j] % mod;
}
void dfs(int x) {
	tr[x].sz = 1;
	for (int i = 0; i < 2; i++) {
		if (!tr[x].son[i]) continue;
		tr[tr[x].son[i]].fa = x;
		dfs(tr[x].son[i]);
		tr[x].sz += tr[tr[x].son[i]].sz;
	}
	for (int j = 0; j <= k; j++) {
		for (int w = 0; w <= j; w++) {
			f[x][j] = (f[x][j] + f[tr[x].son[0]][w] * f[tr[x].son[1]][j - w] % mod) % mod;
		}
	}
	for (int j = k; j >= 0; j--) {
		for (int w = 1; w <= j; w++) {
			f[x][j] = (f[x][j] + f[x][j - w] * fac[w] % mod * C(tr[x].sz - j + w, w) % mod * C(tr[x].key - tr[tr[x].fa].key, w) % mod) % mod;
		}
	}
}
int main() {
	scanf("%d%d", &n, &k);
	inv[0] = inv[1] = 1;
	fac[0] = ifac[0] = 1;
	for (int i = 2; i <= 1000000; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	for (int i = 1; i <= 1000000; i++) fac[i] = fac[i - 1] * i % mod, ifac[i] = ifac[i - 1] * inv[i] % mod;
	for (int i = 1; i <= n; i++) {
		tr[i].val = i;
		scanf("%d", &tr[i].key);
	}
	for (int i = 1; i <= n; i++) {
		while (top > 0 && tr[stk[top]].key > tr[i].key) {
			int lst = stk[top];
			top--;
			if (top && tr[stk[top]].key > tr[i].key) tr[stk[top]].son[1] = lst;
			else tr[i].son[0] = lst;
		}
		stk[++top] = i;
	}
	while (top > 1) {
		tr[stk[top - 1]].son[1] = stk[top];
		top--;
	}
	root = stk[1];
	f[0][0] = 1;
	dfs(root);
	printf("%lld
", f[root][k]);
	return 0;
}

一种更加好写的笛卡尔树建立方法:

for (int i=1; i<=n; i++) {
    while (top>0&&a[stk[top]]>a[i]) ls[i]=stk[top--];
    if (top) rs[stk[top]]=i;
    stk[++top]=i;
}
root=stk[1];
原文地址:https://www.cnblogs.com/zcr-blog/p/14326214.html