zoukankan      html  css  js  c++  java
  • FZU 2282

    题意

    n个人有n把魔杖, 求这n个人至少有k个人拿到属于自己的魔杖的排列方式种数

    思路

    组合数学 + 错排公式

    完全错排公式 : D(n)=(n-1)*(D(n-1)+D(n-2))

    题目意思就是n个数字里要求至少k个数字位置不变,其余进行错排的方案数,容易想到不变的数字从k枚举到n,每次取i(k <= i < n)个出来,对剩下的n-i个进行错排,即C(n, i) * d[n - i] (d[n - i]表示对n-i个数进行错排的方案数),然后将它们累加,但是这样做超时了。
    考虑到n较大,k较小,可以反过来处理,总的方案数为n!,可以从总的方案数里减去不符合条件的情况,即不变的个数从0枚举到k-1

    AC代码

    #include <iostream>
    #include <cstdio>
    
    using namespace std;
    typedef long long ll;
    const ll mod = 1e9+7;
    const int maxn = 1e4+5;
    ll d[maxn];
    ll A[maxn];
    ll C[maxn][100+5];
    
    void init(){
        d[0] = 1, d[1] = 0, d[2] = 1;
        for( int i = 3; i <= 1e4; i++ )  //错排公式
            d[i] = (i-1)*((d[i-2] + d[i-1])%mod) % mod;
        A[0] = 1, A[1] = 1;
        for( int i = 2; i <= 1e4; i++ )  //阶乘
            A[i] = A[i-1]*i % mod;
        C[1][0] = 1, C[1][1] = 1;
        for( int i = 2; i <= 1e4; i++ ){  //组合数
            C[i][0] = 1;
            for( int j = 1; j <= i && j <= 100; j++ )
                C[i][j] = ( C[i-1][j]+C[i-1][j-1] ) % mod;
        }
    }
    
    int main()
    {
        int T, n, k;
        init();
        scanf("%d",&T);
        while(T--){
            scanf("%d%d", &n, &k);
            ll w = 0;
            for( int i = 0; i < k; i++ )
                w = ( w + C[n][i]*d[n-i]%mod ) % mod;
            w = (A[n]-w+mod)%mod;
            printf("%lld
    ",w);
        }
        return 0;
    }
  • 相关阅读:
    数字证书学习笔记
    在微服务中使用领域事件
    用Gradle构建Spring Boot项目
    七言 朱雀
    作为分享者
    Spring Framework学习要点摘抄
    Servlet 3.0/3.1 中的异步处理
    Java集合学习笔记
    Java垃圾回收学习笔记
    你究竟有多了解Spring?
  • 原文地址:https://www.cnblogs.com/JinxiSui/p/9740570.html
Copyright © 2011-2022 走看看