SP3734 PERIODNI - Periodni(笛卡尔树)

据说是一道笛卡尔树经典例题,本蒟蒻拿来加强一下理解的(只简单地总结下为什么用笛卡尔树,以及笛卡尔树的性质,之后树形DP的细节就不讲了),所以想看题解的或许可以转到别的大佬那边去了

链接:https://www.luogu.com.cn/problem/SP3734

其实样例已经说的已经很清楚了,两个数会不会相互影响,取决于中间有没有一个高度比它们都小的矩形把它们隔开,如果我们把矩形高度视为数列,而一个区间最小的矩形高度就是瓶颈值,即是关键的,受这个瓶颈值控制,于是对于这种关键取决于最小值之类的题,我们常常用堆结构来限制,如kruskal重构树,又如本题的笛卡尔树.所以我们以数列编号为BST关键字,矩阵高度为堆关键字,即可把一个不规则图形拆成若干个规则的矩形,

从而解决这道题

/*SP3734 PERIODNI - Periodni*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int maxn = 1e3 + 10;
int lc[maxn],rc[maxn];
int dp[maxn][maxn];
int f[maxn];
int h[maxn];
int st[maxn],top;
int siz[maxn];
bool nrt[maxn];
const int mod = 1e9 + 7;
const int maxsize = 1e6 + 10;
int fa[maxn];
int jc[maxsize],invjc[maxsize];
int n,cnt;
int C(int x,int y){
	return 1ll * jc[x] * invjc[y] % mod * invjc[x-y] % mod;
}
int qpow(int x,int y){
	int ans = 1;
	while(y){
		if(y & 1)	ans = 1ll * ans * x % mod;
		x = 1ll * x * x % mod;
		y >>= 1;
	}
	return ans;
}
int read(){
	char c = getchar();
	int x = 0;
	while(c < '0' || c > '9')		c = getchar();
	while(c >= '0' && c <= '9')		x = x * 10 + c - 48,c = getchar();
	return x;
}
int Add(int x,int y){
	x += y;
	return (x >= mod)?x - mod:x;
}
void treedp(int x){
	siz[x] = 1; 	
	int H = h[x] - h[fa[x]];
	dp[x][0] = 1;
//	cout<<x<<endl;
	if(lc[x]){
		fa[lc[x]] = x,treedp(lc[x]);
		memcpy(f,dp[x],sizeof(f));
		memset(dp[x],0,sizeof(dp[x]));
		for(int i = 0; i <= siz[x]; ++i){
			for(int j = 0; j <= siz[lc[x]]; ++j)
				dp[x][i + j] = Add(dp[x][i + j],1ll * f[i] * dp[lc[x]][j] % mod);
		}
		siz[x] += siz[lc[x]];
	}
	if(rc[x]){
		fa[rc[x]] = x,treedp(rc[x]);
		memcpy(f,dp[x],sizeof(f));
		memset(dp[x],0,sizeof(dp[x]));
		for(int i = 0; i <= siz[x]; ++i){
			for(int j = 0; j <= siz[rc[x]]; ++j)
				dp[x][i + j] = Add(dp[x][i + j],1ll * f[i] * dp[rc[x]][j] % mod);
		}
		siz[x] += siz[rc[x]];
	}
	memcpy(f,dp[x],sizeof(f));
	memset(dp[x],0,sizeof(dp[x]));
	for(int i = 0; i <= siz[x]; ++i){
		if(i > H)	break;
		for(int j = 0; j <= siz[x] - i; ++j){
			dp[x][i+j] = Add(dp[x][i+j],1ll * f[j] * C(siz[x] - j,i) % mod * jc[i] % mod * C(H,i) % mod);
		}
	}
}
int main(){
	jc[0] = invjc[0] = 1;
	int mx = 1e6;
	for(int i = 1; i <= mx; ++i)		jc[i] = 1ll * jc[i-1] * i % mod;
	invjc[mx] = qpow(jc[mx],mod-2);
	for(int i = mx - 1; i >= 0; --i)	invjc[i] = 1ll * invjc[i+1] * (i + 1) % mod;
	n = read(),cnt = read();
	for(int i = 1; i <= n; ++i)		h[i] = read();
	for(int i = 1; i <= n; ++i){
		int k = top;
		while(h[st[k]] > h[i])		k--;
		if(k)	rc[st[k]] = i;
		if(k != top)	lc[i] = st[k+1];
		st[++k] = i;
		top = k;
	}
	for(int i = 1; i <= n; ++i)		nrt[lc[i]] = nrt[rc[i]] = true;
	int rt = 0;
	for(int i = 1; i <= n; ++i){
		if(!nrt[i]){
			rt = i;
			break;
		}
	}
	treedp(rt);
//	cout<<dp[3][0]<<endl;
	printf("%d\n",dp[rt][cnt]);
	return 0;
}

  

posted @ 2020-09-14 21:42  y_dove  阅读(289)  评论(0编辑  收藏  举报