标签:read top ret spl get lld include begin its
一看到是直方图的题目,我们可以联想到笛卡尔树
我们将列数作为 BST 的一维,将高度作为小根堆的一维,这样笛卡尔树上每一个节点都是一个矩形
我们考虑在矩形中选出 k 个合法点的方案数,显然等价于 \(C_{wid}^kC_{hig}^kk!\) ,表示选出 k 种高度和下标并配对,且点之间有标号
然后我们在笛卡尔树上DP,设 \(f_{i,j}\) 表示节点 \(i\) 所代表的子树内选了 \(j\) 个点的方案,转移方程如下:
#include<bits/stdc++.h>
#define inl inline
#define reg register
using namespace std;
namespace zzc
{
typedef long long ll;
inl ll read()
{
ll x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {if(ch==‘-‘)f=-1;ch=getchar();}
while(isdigit(ch)) {x=x*10+ch-48;ch=getchar();}
return x*f;
}
const ll mod = 1e9+7;
const ll maxn = 505;
const ll maxm = 1e6+5;
ll n,k,top;
ll ch[maxn][2],a[maxn],st[maxn],f[maxn][maxn],fac[maxm],inv[maxm],wid[maxn],siz[maxn];
void init()
{
fac[0]=fac[1]=1;
inv[0]=inv[1]=1;
for(reg ll i=2;i<=1000000;i++) fac[i]=fac[i-1]*i%mod,inv[i]=inv[mod%i]*(mod-mod/i)%mod;
for(reg ll i=2;i<=1000000;i++) inv[i]=inv[i]*inv[i-1]%mod;
}
ll C(ll n,ll m)
{
if(n<m) return 0;
return fac[n]*inv[n-m]%mod*inv[m]%mod;
}
void dfs(ll u,ll hig)
{
hig=a[u]-hig;
f[u][0]=1;siz[u]=1;
for(reg ll t=0;t<=1;t++)
{
if(!ch[u][t]) continue;
dfs(ch[u][t],a[u]);
siz[u]+=siz[ch[u][t]];
for(reg ll i=min(siz[u],k);i>=0;i--)
for(reg ll j=1;j<=min(siz[ch[u][t]],i);j++)
f[u][i]=(f[u][i]+f[u][i-j]*f[ch[u][t]][j]%mod)%mod;
}
for(reg ll i=min(siz[u],k);i>=0;i--)
for(reg ll j=1;j<=min(hig,i);j++)
f[u][i]=(f[u][i]+f[u][i-j]*C(siz[u]-(i-j),j)%mod*C(hig,j)%mod*fac[j]%mod)%mod;
}
void work()
{
init();
n=read();k=read();
for(reg ll i=1;i<=n;i++) a[i]=read();
for(reg ll i=1;i<=n;i++)
{
ll j=top;
while(j&&a[st[j]]>a[i]) j--;
if(j) ch[st[j]][1]=i;
if(j<top) ch[i][0]=st[j+1];
top=j;st[++top]=i;
}
dfs(st[1],0);
printf("%lld\n",f[st[1]][k]);
}
}
int main()
{
zzc::work();
return 0;
}
SP3734 PERIODNI - Periodni 笛卡尔树 DP
标签:read top ret spl get lld include begin its
原文地址:https://www.cnblogs.com/youth518/p/14485171.html