2616: SPOJ PERIODNI
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 128 Solved: 48
[Submit][Status][Discuss]
Description
Input
第1行包括两个正整数N,K,表示了棋盘的列数和放的车数。
第2行包含N个正整数,表示了棋盘每列的高度。
Output
包括一个非负整数,表示有多少种放置的方案,输出答案mod
1000000007后的结果即可。
Sample Input
5 2
2 3 1 2 4
2 3 1 2 4
Sample Output
43
HINT
对于100%的数据,有 N≤500,K≤500,h[i] ≤1000000。
Source
我们可以先构造笛卡尔树(每次找最低点分为两半)
之后设f[i][j]表示以i为根的子树共放了j个的方案数。
每次dp时我们处理g[i]表示左右子树共放了i个的方案数。
长为n,宽为m的矩阵放k个车的方案数为C(n,k)*A(m,k)
依据这个每次枚举g[i]转移f即可。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cstdlib> 5 #include<cmath> 6 #include<algorithm> 7 #define LL long long 8 #define mod 1000000007 9 using namespace std; 10 LL n,k; 11 LL g[1005],a[505]; 12 LL f[505][505]; 13 LL p[1000005],inv[1000005]; 14 LL cal(LL x,LL y,LL k){ 15 if(x<k||y<k)return 0; 16 return p[x]*inv[x-k]%mod*inv[k]%mod*p[y]%mod*inv[y-k]%mod; 17 } 18 LL dp(int l,int r,int h) { 19 if(l>r) return 0; 20 int lowi=l; 21 for(int i=l;i<=r;i++) if(a[lowi]>a[i]) lowi=i; 22 int la=dp(l,lowi-1,a[lowi]),ra=dp(lowi+1,r,a[lowi]); 23 memset(g,0,sizeof(g)); 24 for(int i=0;i<=lowi-l;i++) 25 for(int j=0;j<=r-lowi;j++) g[i+j]=(g[i+j]+f[la][i]*f[ra][j])%mod; 26 for(int i=0;i<=r-l+1;i++) 27 for(int j=0;j<=i;j++) 28 f[lowi][i]=(f[lowi][i]+g[j]*cal(r-l+1-j,a[lowi]-h,i-j))%mod; 29 return lowi; 30 } 31 LL power(LL x,LL ad) { 32 LL ans=1; 33 while(ad) { 34 if(ad&1) ans=ans*x%mod; 35 x=x*x%mod; 36 ad>>=1; 37 } 38 return ans; 39 } 40 int main() { 41 p[0]=1; 42 for(int i=1;i<=1000000;i++) p[i]=p[i-1]*i%mod; 43 inv[1000000]=power(p[1000000],mod-2); 44 for(int i=1000000-1;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod; 45 scanf("%lld%lld",&n,&k); 46 for(int i=1;i<=n;i++) scanf("%lld",&a[i]); 47 f[0][0]=1; 48 printf("%lld ",f[dp(1,n,0)][k]); 49 }