[BZOJ2616]SPOJ PERIODNI

题目描述

即给出\(n\)\(1*h_i\)的矩阵,在一条直线上对齐下表面,求放置\(k\)个互不攻击的车的方案数。

答案有可能很大,所以输出答案对\(1000000007\)取模。

Input

\(1\)行包括两个正整数\(N,K\),表示了棋盘的列数和放的车数。
\(2\)行包含\(N\)个正整数,表示了棋盘每列的高度。

对于\(100\%\)的数据,有 \(N≤500,K≤500,h[i] ≤1000000\)

Output

包括一个非负整数,表示有多少种放置的方案.

Sample Input

5 2 
2 3 1 2 4 

Sample Output

43

题意:即给出\(n\)\(1*h_i\)的矩阵,在一条直线上对齐下表面,求放置\(k\)个互不攻击的车的方案数。

显然是先建出小根笛卡尔树,考虑每个矩形内部的答案。

不会笛卡尔树请转到笛卡尔树学习笔记

\(dp[u][i]\) 表示 \(u\) 子树内放 \(i\) 个数的方案数, \(dp1[i]\) 表示 当前子树\(u\)内不考虑当前矩形,放 \(i\) 个数的方案数,设\(H[i]\)为当前矩阵可行高度(即\(A[u]-A[fa[u]]\))。

显然有 \(dp1[] = f[ls]*f[rs]\),即左右子树的卷积。

接下来就是背包的转移了,枚举当前矩形内有多少列还是空的进行转移。

设当前子树放置\(i\)个棋子,有\(j\)个在当前矩阵放置。

\(dp[u][i]+=\sum_{j=0}^idp1[i-j]*C(Sz[u]-(i-j),j)*C(H[x],j)*j!\)

第一个组合数是枚举矩阵所剩的行,第二个组合数是枚举矩阵所剩的列。

最后乘上 j! 是因为横纵坐标是两两组合的,因此匹配的方案数为 j!。

#include <bits/stdc++.h>
 
using namespace std;
 
#define int long long
#define reg register
#define Raed Read
#define clr(a,b) memset(a,b,sizeof a)
#define Mod(x) (x>=mod)&&(x-=mod)
#define debug(x) cerr<<#x<<"="<<x<<endl;
#define debug2(x,y) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<endl;
#define debug3(x,y,z) cerr<<#x<<"="<<x<<" "<<#y<<"="<<y<<" "<<#z<<"="<<z<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(int i=(G).Head[x]; i; i=(G).Nxt[i])
 
inline int Read(void) {
    int res=0,f=1;
    char c;
    while(c=getchar(),c<48||c>57)if(c=='-')f=0;
    do res=(res<<3)+(res<<1)+(c^48);
    while(c=getchar(),c>=48&&c<=57);
    return f?res:-res;
}
 
template<class T>inline bool Min(T &a, T const&b) {
    return a>b?a=b,1:0;
}
template<class T>inline bool Max(T &a, T const&b) {
    return a<b?a=b,1:0;
}
const int N=505,M=1e6+5,mod=1e9+7;
 
bool MOP1;
 
int Ans,n,K,stk[N],A[N],son[N][2];
 
int Fac[M],Inv[M],V[M];
 
int C(int a,int b) {
    if(a<b)return 0;
    return Fac[a]*(Inv[a-b]*Inv[b]%mod)%mod;
}
 
int Sz[N],dp1[N],dp[N][N],H[N];
void dfs(int x) {
    Sz[x]=dp[x][0]=1;
    rep(i,0,1)if(son[x][i]) {
        int y=son[x][i];
        dfs(y);
        clr(dp1,0);
        rep(j,0,min(Sz[y],K))rep(k,0,min(Sz[x],K))if(j+k<=K) {
            dp1[j+k]+=dp[x][k]*dp[y][j]%mod,Mod(dp1[j+k]);
        }
        Sz[x]+=Sz[y];
        rep(j,0,min(Sz[x],K))dp[x][j]=dp1[j];
    }
    rep(j,0,min(Sz[x],K))dp1[j]=dp[x][j];
    rep(i,0,min(Sz[x],K)) {
        int temp=0;
        rep(j,0,i) {
            temp+=dp1[i-j]*Fac[j]%mod*C(Sz[x]-(i-j),j)%mod*C(H[x],j)%mod;
            Mod(temp);
        }
        dp[x][i]=temp;
    }
 
}
 
bool MOP2;
 
void _main(void) {
    Fac[0]=Inv[0]=Fac[1]=V[1]=Inv[1]=1;
    ret(i,2,M) {
        Fac[i]=Fac[i-1]*i%mod;
        V[i]=(mod-mod/i)*V[mod%i]%mod;
        Inv[i]=Inv[i-1]*V[i]%mod;
    }
    n=Read(),K=Read();
    rep(i,1,n)A[i]=Read(),son[i][0]=son[i][1]=0;
    int top=0;
    rep(i,1,n) {
        while(top&&A[stk[top]]>A[i])son[i][0]=stk[top--];
        if(top)son[stk[top]][1]=i;
        stk[++top]=i;
    }
    rep(i,0,1)rep(j,1,n)if(son[j][i])H[son[j][i]]=A[son[j][i]]-A[j];
    H[stk[1]]=A[stk[1]];
    dfs(stk[1]);
    printf("%d\n",dp[stk[1]][K]);
}
 
signed main() {
    _main();
    return 0;
}
posted @ 2019-09-08 15:32  dsjkafdsaf  阅读(292)  评论(0编辑  收藏  举报