据说是一道笛卡尔树经典例题,本蒟蒻拿来加强一下理解的(只简单地总结下为什么用笛卡尔树,以及笛卡尔树的性质,之后树形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
",dp[rt][cnt]);
return 0;
}