[FJOI2016] 建筑师 题解

显然有一个 \(dp\) 思路。设 \(f_{i,j}\) 表示现在修了 \(i\) 栋楼,从第一栋楼外侧能看到 \(j\) 栋楼的方案数,显然有:

\[f_{i,j}=\begin{cases}[i=0](j=0)\\f_{i-1,j-1}+(i-1)f_{i-1,j}(j\ne 0)\end{cases} \]

一眼 \(f_{i,j}=\begin{bmatrix}i\\j\end{bmatrix}\)。那么答案即为:

\[\sum_{i=0}^{n-1}\begin{bmatrix}i\\A-1\end{bmatrix}\begin{bmatrix}n-i-1\\B-1\end{bmatrix}\binom ni \]

但这样时间复杂度是 \(O(tn)\) 的,并不能 \(AC\)。考虑组合意义。相当于拿出了 \(A+B-2\) 个圆排列,再挑出了其中的 \(A-1\) 个圆排列。这个式子可以表示为:

\[\begin{bmatrix}n-1\\A+B-2\end{bmatrix}\binom{A+B-2}{A-1} \]

时间复杂度 \(O(n(A+B)+t)\)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=50005,M=105,p=1e9+7;
int t,n,str[N][M*2],C[M*2][M];
signed main(){
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);
    cin>>t,str[0][0]=1;
    for(int i=1;i<=5e4;i++) for(int j=1;j<=200;j++)
        str[i][j]=(str[i-1][j-1]+str[i-1][j]*(i-1))%p;
    for(int i=0;i<=200;i++){
        C[i][0]=1;
        for(int j=1;j<=min(i,100ll);j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
    }while(t--){
        int n,a,b;cin>>n>>a>>b;
        cout<<str[n-1][a+b-2]*C[a+b-2][a-1]%p<<"\n";
    }return 0;
}
posted @ 2025-01-23 09:42  长安一片月_22  阅读(11)  评论(0)    收藏  举报