SP3734 PERIODNI - Periodni
考虑到如果只有一个矩形的话我们可以很方便的求出答案。
那么我们可以通过对序列进行建笛卡尔树来做到划分矩形。
注意在dp时,子矩阵的合并类似于背包。
考虑父节点时考虑已经选过的列。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define maxn 5010
#define mod 1000000007
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
ll n,k,top,root;
ll ls[maxn],rs[maxn],st[maxn];
ll f[maxn][maxn],h[maxn],siz[maxn],fac[1000050],inv[1000050];
ll qp(ll x,ll y)
{
ll ans=1;
while(y)
{
if(y&1) ans=(ans*x)%mod;
x=(x*x)%mod;
y>>=1;
}
return ans%mod;
}
void init()
{
fac[0]=fac[1]=inv[0]=inv[1]=1;
fac[2]=2,inv[2]=qp(2,mod-2);
for(int i=3;i<=1000000;++i)
{
fac[i]=(fac[i-1]*i)%mod;
inv[i]=qp(fac[i],mod-2);
}
}
ll C(ll n,ll m)
{
if(n<m) return 0;
return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int build()
{
for(int i=1;i<=n;++i)
{
while(top&&h[st[top]]>h[i]) ls[i]=st[top--];
if(top) rs[st[top]]=i;
st[++top]=i;
}
return st[1];
}
void dfs(int x,int val)
{
f[x][0]=siz[x]=1;
ll high=h[x]-val;
if(ls[x])
{
ll y=ls[x];
dfs(y,h[x]),siz[x]+=siz[y];
for(ll i=min(siz[x],k);i>=0;--i)
for(ll j=1;j<=min(siz[y],i);++j)
f[x][i]=(f[x][i]+f[y][j]*f[x][i-j]%mod)%mod;
}
if(rs[x])
{
ll y=rs[x];
dfs(y,h[x]),siz[x]+=siz[y];
for(ll i=min(siz[x],k);i>=0;--i)
for(ll j=1;j<=min(siz[y],i);++j)
f[x][i]=(f[x][i]+f[y][j]*f[x][i-j]%mod)%mod;
}
for(ll i=min(siz[x],k);i>=0;--i)
for(ll j=1;j<=min(high,i);++j)
f[x][i]=(f[x][i]+f[x][i-j]*fac[j]%mod*C(high,j)%mod*C(siz[x]-(i-j),j)%mod)%mod;
}
int main()
{
init();
read(n),read(k);
for(int i=1;i<=n;++i) read(h[i]);
root=build();
dfs(root,0);
printf("%lld",f[root][k]);
return 0;
}