4. Stirling Number(Second Kind)

4. Stirling Number(Second Kind)

S(n, m)表示含 n 个元素的集合划分为 m 个集合的情况数 或者是n个有标号的球放到m 个无标号的盒子中, 要求无一为空, 其不同的 方案数 

4. Stirling Number(Second Kind)_第1张图片

例题:

1.51NOD1250

题目描述:

你有一个初始为1到n的顺序数组
问题一:恰好进行k次相邻交换,最后有多少不同的排列
问题二:进行不多于k次交换(不一定相邻),最后有多少种不同的排列
(n,k<=3000)(n,k<=3000)

题解:

对于第一问,看到交换相邻两项,很容易联想到冒泡排序。
我们知道,冒泡排序的本质是通过交换相邻项来使逆序对数不断-1。
所以显然在本题中一次交换相邻项会增加或减少一对逆序对。
所以经过k次交换后逆序对个数与k在模2的意义下相等(即有k-2*x个逆序对)。
我们可以通过DP轻易地求出方案数。
令f[i][j]表示有j个逆序对的1~i的排列数。
显然有f[i][j]=f[i-1][j]+f[i-1][j-1]+...+f[i-1][j-i+1]。
通过前缀和优化可以优化到O(n^2),可以使用滚动数组优化掉一维。
答案S1为f[n][k]+f[n][k-2]+f[n][k-4]+...+f[n][k%2]。

对于第二问,我们可以从每个位置的编号向这个位置上的数连一条边。
一开始,一共有n个环(全是自环)。
通过少量的手玩,每交换两项后重新按上述规则连边会使环的数量加1或减1。
但题目要求的是交换不超过k次所以我们只需考虑环的数量的上下界即可。
令g[i][j]为按上述规则连边后环的数量为j的1~i的排列数,这是第一类斯特林数
于是套斯特林数递推公式g[i][j]=g[i-1][j-1]+(i-1)*g[i-1][j]。
这里同样可以使用滚动数组。
答案S2则是的g[n][n-k]+g[n][n-k+1]+...+g[n][n-1]+g[n][n]。

#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
typedef long long LL;
const int MAXN=3005;
const LL MOD=1e9+7;
int n,k;
LL ans1,ans2;
LL f[MAXN],sum[MAXN];
LL g[MAXN];
int main(){
    scanf("%d%d",&n,&k);
    f[0]=1;for(int i=0;i<=k;i++) sum[i]=1;
    for(int i=1;i<=n;i++){
        for(int j=k;j;j--) f[j]=(sum[j]-sum[j-i]+MOD)%MOD;
        sum[0]=f[0]=1;
        for(int j=1;j<=k;j++) sum[j]=(sum[j-1]+f[j])%MOD;
    }
    for(int i=k%2;i<=k;i+=2) ans1=(ans1+f[i])%MOD;
    g[0]=1;
    for(int i=1;i<=n;i++){
        for(int j=i;j;j--)
            g[j]=(g[j-1]+g[j]*(i-1))%MOD;
        g[0]=0;
    }
    for(int i=n-k;i<=n;i++) ans2=(ans2+g[i])%MOD;
    printf("%lld %lld\n",ans1,ans2);
    return 0;
}

 

2.HDU4372

题意:N座高楼,高度均不同且为1~N中的数,从前向后看能看到F个,从后向前看能看到B个,问有多少种可能的排列数。

0 < N, F, B <= 2000

题解:

先我们知道一个结论:n的环排列的个数与n-1个元素的排列的个数相等,因为P(n,n)/n=(n-1)!。
可以肯定,无论从最左边还是从最右边看,最高的那个楼一定是可以看到的.
假设最高的楼的位置固定,最高楼的编号为n,那么我们为了满足条件,可以在楼n的左边分x-1组,右边分y-1组,且用每
组最高的那个元素代表这一组,那么楼n的左边,从左到右,组与组之间最高的元素一定是单调递增的,且每组中的最高元
素一定排在该组的最左边,每组中的其它元素可以任意排列(相当于这个组中所有元素的环排列)。右边反之亦然。
然后,可以这样考虑这个问题,最高的那个楼左边一定有x-1个组,右边一定有y-1个组,且每组是一个环排列,这就引出
了第一类Stirling数(个人分成组,每组内再按特定顺序围圈的分组方法的数目)。
--------------------- 
作者:acdreamers 
来源:CSDN 
原文:https://blog.csdn.net/acdreamers/article/details/9732431 

#include 
#include 
#include 
 
using namespace std;
typedef long long LL;
 
const int N = 2005;
const LL MOD = 1000000007;
 
LL C[N][N];
LL S[N][N];
 
void Init() {
    int i, j;
    for(i = 0; i < N; i++) {
        C[i][0] = 1;
        C[i][i] = 1;
        S[i][0] = 0;
        S[i][i] = 1;
        for(j = 1; j < i; j++) {
            C[i][j] = (C[i - 1][j]%MOD + C[i - 1][j - 1] % MOD) % MOD;
            S[i][j] = ((i - 1) % MOD * S[i - 1][j] % MOD + S[i - 1][j - 1] % MOD);
        }
    }
}
 
int main() {
    LL t, n, f, b, ans;
    Init();
    scanf("%I64d", &t);
    while(t--) {
        scanf("%I64d%I64d%I64d", &n, &f, &b);
        ans = C[f + b - 2][f - 1] % MOD * S[n - 1][f + b - 2] % MOD;
        printf("%I64d\n", ans);
    }
    return 0;
}

 

你可能感兴趣的:(模板)