小半个月前的测试,现在翻出来。
考试时我和sxyA了这题。
当时随便搞了个dp,dp[i][j]表示i个数能看到j个的情况数,考虑新加入一个比之前i-1个数都小的数,能看到它的情况是它加到第一个,不能看到它的情况是它加到第1~i-1个数之后。所以 dp[i][j]=dp[i-1][j-1]*1+dp[i-1][j]*(i-1);
然而这个东西刚好就是第一类斯特林数。
第一类Stirling数是有正负的,其绝对值是包含n个元素的集合分作k个环排列的方法数目。
i个数的排列可以看到j个数的情况可以看作 把i个数分成j个集合,每个集合中最大的数排在第一个,其它的数任意排列。而这刚好是一个环排列。
显然,n个数的环排列等于n-1个数的全排列。
//Achen
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
const int N=5e4+7;
const int mod=1e9+7;
typedef long long LL;
using namespace std;
LL dp[N][207],C[207][207];
int T,n,a,b;
template<typename T> void read(T &x) {
char ch=getchar(); T f=1; x=0;
while(ch!=‘-‘&&(ch<‘0‘||ch>‘9‘)) ch=getchar();
if(ch==‘-‘) f=-1,ch=getchar();
for(;ch>=‘0‘&&ch<=‘9‘;ch=getchar()) x=x*10+ch-‘0‘; x*=f;
}
#define orzllj
int main() {
#ifdef orzllj
freopen("building.in","r",stdin);
freopen("building.out","w",stdout);
#endif
dp[1][1]=1;
for(int i=2;i<=50000;i++)
for(int k=1;k<=min(200,i);k++)
dp[i][k]=(dp[i-1][k]*(i-1)%mod+dp[i-1][k-1])%mod;
for(int i=0;i<=200;i++) C[i][0]=1;
for(int i=1;i<=200;i++)
for(int j=1;j<=i;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
read(T);
while(T--) {
read(n); read(a); read(b);
LL ans=dp[n-1][a+b-2];
ans=(ans*C[a+b-2][a-1])%mod;
printf("%lld\n",ans);
}
return 0;
}
/*
2
3 2 2
3 2 1
*/
顺便:
第二类Stirling数实际上是集合的一个拆分,表示将n个不同的元素拆分成m个集合的方案数,
S(n,k)=s(n-1,k-1)+S(n-1,k)*k; 递推公式很好想。
通项公式:
其它的之后什么时候再学吧。