fzu2282 wand 排列組合 錯排

點擊打開fzu2282

題意:n個巫師參加會議,每個人有一根屬於自己的魔杖。有一個巫師想打亂原本魔杖的排列順序,問至少有k個人拿對的打亂方法有多少種。

思路:

題目要求至少有K個人拿對那麼就是說拿對的人可以爲k,k+1,....n.例如n=5,k=3,那麼可以有3,4,5個人拿對。

解決:

n根魔杖的所有排列方式爲A(n,n),所以只需要用所有的減去1,2。。。k個人拿錯的情況就可以了。至於爲什麼不累加從k個人排錯到n,是因爲n較大會超時(錯了才知道)。

所以只需要累加1,2..k個人拿錯的情況就行了。列如k個人拿錯,首先需要從n個人中選出k個人去哪錯,即C(n,k),然後其他n-k個人排錯,即乘錯排數。所以k個人拿錯的的結果爲

C(n,k)*a[n-k].(0<=k<=n)

排列組合公式:


下面是代碼,其中求組合數用了費小馬定理把除法變成了相乘,優化了算法。

費小馬定理:假如p是質數,且gcd(a,p)=1,那麼 a(p-1)≡1(mod p),即:假如a是整數,p是質數,且a,p互質(即兩者只有一個公約數1),那麼a的(p-1)次方除以p的餘數恆等於1。因爲a*a^-1=1=a^(p-1)%p,所以a^-1=a^(p-2)%p.

#include<stdio.h>
#include<iostream>
#include<cstring>
using namespace std;
#define ll  long long
#define mod 1000000007
ll a[10005],b[10005];
void init()
{

	a[0] = 1;
	a[1] = 0;
	a[2] = 1;
	b[1] = 1;
	b[2] = 2;
	for (int i = 3; i <= 10000; i ++)
    {
		a[i] = (((i - 1) % mod) * ((a[i - 2] + a[i - 1])) % mod) % mod;
		b[i] = ((b[i - 1] % mod) * (i % mod)) % mod;
	}


}
ll qpow(ll a, ll x) ///快速冪
{
    ll res = 1;
    while(x)
    {
        if (x & 1)
        {
            res = (res * a) %mod;
        }
        a = (a * a) % mod;
        x >>= 1;
    }
    return res;
}

ll C(ll n, ll k) ///求組合數模版
{
    if(n < k)
    {
        return 0;
    }

    if(k > n - k)
    {
        k = n - k;
    }

    ll a = 1, b = 1;

    for(int i = 0; i < k; i++)
    {
        a = a * (n - i) % mod;  ///n*(n-1)*(n-2).....*k
        b = b * (i + 1) % mod;  ///1*2*...*k
    }

    return a * qpow(b, mod - 2) % mod;  ///費小馬定理
}
int main()
{
    int n,m;
    int t;
    scanf("%d",&t);
    init();
    while(t--)
    {
       scanf("%d %d",&n,&m);
       ll sum=b[n];
       ll ans=0;
        for(int i=0;i<m;i++)
        {
            ans=( (ans%mod) + ((C(n,i)*a[n-i])%mod) )%mod;
        }
       // cout<<ans<<endl;
       printf("%lld\n",(mod +(sum%mod)-(ans%mod)  )%mod);
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章