Analysis
大多数题解都提供的是dp做法
这里推广一个不知哪个神犇想出来的神仙做法
O(1)计算可还行?
除了最高的那根杆,左边还能看到 l - 1 根,右边还能看到 r - 1 根。
把这些杆子和他们挡住的杆子看做一个圆排列,那么共有
[
l
+
r
−
2
n
−
1
]
[^{n-1}_{l+r-2}]
[l+r−2n−1]种分配方案。
左边要选
l
−
1
l-1
l−1根
故答案为
a
n
s
=
[
l
+
r
−
2
n
−
1
]
∗
(
l
−
1
l
+
r
−
2
)
ans=[^{n-1}_{l+r-2}]*(_{l-1}^{l+r-2})
ans=[l+r−2n−1]∗(l−1l+r−2)
可以这样来意会:
对于一段区间(当前最高的杆子和它后面低于它的杆子),长度为k,其最高的杆子已经确定,那么后面就有
(
k
−
1
)
!
(k-1)!
(k−1)!种排列方式,这和圆排列恰好神奇的吻合
然后从
l
+
r
−
2
l+r-2
l+r−2中选取
l
−
1
l-1
l−1个圆排列放在左边,一定可以找出一种且仅有一种排列方式,使得其满足,从左往右,最高杆子的高度依次递增。对于右边,同理。
Code
注意第一类斯特林数的计算,外层循环从0开始
S1(0,0)=1
(从0个中选0个方案数为1)
#include<bits/stdc++.h>
#define re register
#define int long long
using namespace std;
typedef long long ll;
ll S1[210][210],C[210][210];
void pre_work(){
C[0][0]=1;
for(re int i=1;i<=205;++i){
C[i][i]=C[i][0]=1;
for(re int j=1;j<i;++j){
C[i][j]=(C[i-1][j-1]+C[i-1][j]);
}
}
for(re int i=0;i<=205;++i){
S1[i][i]=1;
for(re int j=1;j<i;++j){
S1[i][j]=(S1[i-1][j-1]+(i-1)*S1[i-1][j]);
}
}
}
signed main(){
int T;
scanf("%lld",&T);
pre_work();
while(T--){
int n,l,r;
scanf("%lld%lld%lld",&n,&l,&r);
printf("%lld\n",S1[n-1][l+r-2]*C[l+r-2][l-1]);
}
return 0;
}