[FJOI2016]建筑师

Description:

小 Z 是一个很有名的建筑师,有一天他接到了一个很奇怪的任务:在数轴上建 \(n\) 个建筑,每个建筑的高度是 \(1\)\(n\) 之间的一个整数。
小 Z 有很严重的强迫症,他不喜欢有两个建筑的高度相同。另外小 Z 觉得如果从最左边(所有建筑都在右边)看能看到 \(A\) 个建筑,从最右边(所有建筑都在左边)看能看到 \(B\) 个建筑,这样的建筑群有着独特的美感。现在,小 Z 想知道满足上述所有条件的建筑方案有多少种?

Hint:

\(T \le 2*10^5 ,n \le 5*10^4 ,A,B \le 100\)

Solution:

只会暴力dp......

如果熟悉斯特林数,这题应该很好想吧

我们把被看到的建筑及它其挡住的建筑看做整体

除去最高的一座,有\(A-1\)座在左边,\(B-1\)座在右边

再枚举左右

答案就是\(S(n-1,A+B-2)*C(A+B-2,A-1)\)

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define ls p<<1 
#define rs p<<1|1
using namespace std;
typedef long long ll;
const ll mxn=5e4,mod=1e9+7;
ll n,m,T,cnt,hd[mxn],fac[mxn+5],ifac[mxn+5];
ll s[mxn+5][205];

inline ll read() {
    char c=getchar(); ll x=0,f=1;
    while(c>'9'||c<'0') {if(c=='-') f=-1;c=getchar();}
    while(c<='9'&&c>='0') {x=(x<<3)+(x<<1)+(c&15);c=getchar();}
    return x*f;
}
inline void chkmax(ll &x,ll y) {if(x<y) x=y;}
inline void chkmin(ll &x,ll y) {if(x>y) x=y;}

struct ed {
    ll to,nxt;
}t[mxn<<1];

inline void add(ll u,ll v) {
    t[++cnt]=(ed) {v,hd[u]}; hd[u]=cnt;
}

ll qpow(ll a,ll b) {
    ll res=1,bs=a;
    while(b) {
        if(b&1) res=1ll*res*bs%mod;
        bs=1ll*bs*bs%mod;
        b>>=1;
    }
    return res;
}

ll C(ll n,ll m) {
    return 1ll*fac[n]*ifac[m]%mod*ifac[n-m]%mod;
}

int main()
{
    T=read(); fac[0]=ifac[0]=1; ll n,A,B; s[0][0]=s[1][1]=1;
    for(ll i=1;i<=mxn;++i) fac[i]=1ll*fac[i-1]*i%mod; 
    ifac[mxn]=qpow(fac[mxn],mod-2);
    for(ll i=mxn-1;i>=1;--i) ifac[i]=1ll*ifac[i+1]*(i+1)%mod;
    for(ll i=2;i<=mxn;++i) s[i][1]=1ll*s[i-1][1]*(i-1)%mod;
    for(ll i=2;i<=mxn;++i) 
        for(ll j=1;j<=i&&j<=200;++j)
            s[i][j]=(s[i-1][j-1]+1ll*s[i-1][j]*(i-1)%mod)%mod;
    while(T--) {
        n=read(); A=read(); B=read();
        printf("%lld\n",1ll*s[n-1][A+B-2]*C(A+B-2,A-1)%mod);
    }
    return 0;
}

posted @ 2019-03-22 20:37  cloud_9  阅读(132)  评论(0编辑  收藏  举报