BZOJ2616 : SPOJ PERIODNI

长为$A$,宽为$B$的矩阵放$K$个车的方案数$=C(A,K)\times C(B,K)\times K!$。

建立笛卡尔树,那么左右儿子独立,设$f[i][j]$表示$i$子树内放$j$个车的方案数。

合并左右儿子之后,枚举在底部矩形放几个车进行转移即可。

时间复杂度$O(n^3)$。

 

#include<cstdio>
const int N=505,M=1000005,P=1000000007;
int n,m,i,a[N],mx,fac[M],inv[M],g[N],f[N][N];
inline int cal(int a,int b,int k){
  if(a<k||b<k)return 0;
  return 1LL*fac[a]*inv[a-k]%P*inv[k]%P*fac[b]%P*inv[b-k]%P;
}
int dp(int l,int r,int h){
  if(l>r)return 0;
  int i,j,x=l;
  for(i=l;i<=r;i++)if(a[i]<a[x])x=i;
  int u=dp(l,x-1,a[x]),v=dp(x+1,r,a[x]);
  for(i=0;i<=r-l;i++)g[i]=0;
  for(i=0;i<=x-l;i++)if(f[u][i])for(j=0;j<=r-x;j++)g[i+j]=(1LL*f[u][i]*f[v][j]+g[i+j])%P;
  for(i=0;i<=r-l+1;i++)for(f[x][i]=j=0;j<=r-l&&j<=i;j++)f[x][i]=(1LL*g[j]*cal(r-l+1-j,a[x]-h,i-j)+f[x][i])%P;
  return x;
}
int main(){
  scanf("%d%d",&n,&m);
  for(mx=n,i=1;i<=n;i++)scanf("%d",&a[i]),mx=a[i]>mx?a[i]:mx;
  for(fac[0]=i=1;i<=mx;i++)fac[i]=1LL*fac[i-1]*i%P;
  for(inv[0]=inv[1]=1,i=2;i<=mx;i++)inv[i]=1LL*P/i*(P-inv[P%i])%P;
  for(i=2;i<=mx;i++)inv[i]=1LL*inv[i-1]*inv[i]%P;
  return printf("%d",f[dp(f[0][0]=1,n,0)][m]),0;
}

  

posted @ 2017-03-01 17:27  Claris  阅读(370)  评论(0编辑  收藏  举报