版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
题目描述:
N≤500,K≤500,h[i] ≤1000000,mod 109+7。
题目分析:
根据图形比较容易想到按照最小值划分区间,那么 *n这一个矩形区域就由这个最小值控制,两边的比 高的车显然不会互相影响,据此就可以想到一个根据最小值划分然后DP的做法。
划分的过程就是笛卡尔树的形态,所以问题就变成了在笛卡尔树上DP, 表示 子树选了 个车的方案数,枚举左右子树以及当前矩形用点转移即可。
Code:
#include<bits/stdc++.h>
#define maxn 505
#define maxh 1000005
using namespace std;
const int mod = 1e9+7;
int n,m,h[maxn],siz[maxn],f[maxn][maxn],tmp[maxn],fac[maxh],inv[maxh];
int lc[maxn],rc[maxn],S[maxn],tp;
inline int C(int n,int m){return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;}
void dfs(int u,int dep){
if(!u) return;
dfs(lc[u],h[u]),dfs(rc[u],h[u]);//(h[u]-dep)*siz[i]就是当前点的矩形范围
siz[u]=siz[lc[u]]+siz[rc[u]]+1;
for(int i=0;i<=siz[u];i++) tmp[i]=0;
for(int i=siz[lc[u]];i>=0;i--)
for(int j=siz[rc[u]];j>=0;j--)
tmp[i+j]=(tmp[i+j]+1ll*f[lc[u]][i]*f[rc[u]][j])%mod;//提前将儿子用的总个数为j的方案算出来
for(int i=0;i<=siz[u];i++)
for(int j=max(0,i-(h[u]-dep));j<=i;j++)
f[u][i]=(f[u][i]+1ll*C(siz[u]-j,i-j)*C(h[u]-dep,i-j)%mod*fac[i-j]%mod*tmp[j])%mod;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&h[i]);//建笛卡尔树,维护右链的单调栈,很简洁
while(tp&&h[i]<=h[S[tp]]) lc[i]=S[tp--];
tp&&(rc[S[tp]]=i), S[++tp]=i;
}
const int N = max(n,*max_element(h+1,h+1+n));
fac[0]=fac[1]=inv[0]=inv[1]=1;
for(int i=2;i<=N;i++) fac[i]=1ll*fac[i-1]*i%mod,inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i=2;i<=N;i++) inv[i]=1ll*inv[i]*inv[i-1]%mod;
f[0][0]=1,dfs(S[1],0);
printf("%d\n",f[S[1]][m]);
}