【组合数+全错排】fzuoj 2282 Wand

来源:互联网 发布:北京可以备案的域名 编辑:程序博客网 时间:2024/06/05 06:24

Problem 2282 Wand

Accept: 31    Submit: 95
Time Limit: 1000 mSec    Memory Limit : 262144 KB

 Problem Description

N wizards are attending a meeting. Everyone has his own magic wand. N magic wands was put in a line, numbered from 1 to n(Wand_i owned by wizard_i). After the meeting, n wizards will take a wand one by one in the order of 1 to n. A boring wizard decided to reorder the wands. He is wondering how many ways to reorder the wands so that at least k wizards can get his own wand.

For example, n=3. Initially, the wands are w1 w2 w3. After reordering, the wands become w2 w1 w3. So, wizard 1 will take w2, wizard 2 will take w1, wizard 3 will take w3, only wizard 3 get his own wand.

 Input

First line contains an integer T (1 ≤ T ≤ 10), represents there are T test cases.

For each test case: Two number n and k.

1<=n <=10000.1<=k<=100. k<=n.

 Output

For each test case, output the answer mod 1000000007(10^9 + 7).

 Sample Input

2
1 1
3 1

 Sample Output

1
4

 Source

第八届福建省大学生程序设计竞赛-重现赛(感谢承办方厦门理工学院)

Submit  Back  Status  Discuss

题意:n个巫师对应n个魔法棒,问让至少k个巫师拿对自己的魔法棒有多少种重排方案;

思路:从n中选k个组合,剩下n-k个全错排;全错排公式:a[i]=(i-1)*(a[i-1]+a[i-2]); 相同思路的题目nyoj451光棍节的快乐

代码1:

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define mod 1000000007const int maxn=10004;typedef long long ll;ll f[maxn+10];ll fac[maxn+10];void init(){    fac[0]=1,fac[1]=1,fac[2]=2;      //预处理阶乘    for(ll i=3;i<=maxn;i++)    {        fac[i]=(fac[i-1]*i*1LL)%mod;        fac[i]%=mod;    }}ll inv(ll a)        //求a对mod的逆元{    return a==1?1:inv(mod%a)*(mod-mod/a)%mod;}ll C(ll n,ll m)     //组合数{    return fac[n]*inv(fac[m])%mod*inv(fac[n-m])%mod;}int main(){    int T;    f[0]=1,f[1]=0,f[2]=1,f[3]=2;    for(ll i=4;i<=maxn;i++)    {        f[i]=(i-1)*((f[i-1]%mod+f[i-2]%mod)%mod);        f[i]%=mod;    }    init();    scanf("%d",&T);    while(T--)    {        ll n,k;        scanf("%I64d%I64d",&n,&k);        ll res=0;        for(int i=k;i<=n;i++)        {            ll res1=C(n,i)%mod;            ll res2=f[n-i]%mod;       //            printf("%I64d %I64d\n",res1,res2);            res+=(res1*res2)%mod;            res%=mod;        }        printf("%I64d\n",res);    }    return 0;}//耗时109ms


代码2

#include<cstdio>#include<cstring>#include<algorithm>using namespace std;#define mod 1000000007const int maxn=10004;typedef long long ll;ll f[maxn+10],inv[maxn+10];ll fac[maxn+10];ll mod_pow(ll x,ll n){    ll res=1;    while(n>0)    {        if(n&1)            res=res*x%mod;        x=x*x%mod;        n>>=1;    }    return res%mod;}void init(){    fac[0]=1,fac[1]=1,fac[2]=2;    for(ll i=3;i<=maxn;i++)    {        fac[i]=(fac[i-1]*i*1LL)%mod;        fac[i]%=mod;    }    inv[maxn]=mod_pow(fac[maxn],mod-2);   //预处理阶乘的逆元    for(ll i=maxn-1;i>=0;i--)        inv[i]=(inv[i+1]*(i+1))%mod;}ll C(ll n,ll m){    return fac[n]*inv[m]%mod*inv[n-m]%mod;}int main(){    int T;    f[0]=1,f[1]=0,f[2]=1,f[3]=2;    for(ll i=4;i<=maxn;i++)    {        f[i]=(i-1)*((f[i-1]%mod+f[i-2]%mod)%mod);        f[i]%=mod;    }    init();    scanf("%d",&T);    while(T--)    {        ll n,k;        scanf("%I64d%I64d",&n,&k);        ll res=0;        for(int i=k;i<=n;i++)        {            ll res1=C(n,i)%mod;            ll res2=f[n-i]%mod;//            printf("%I64d %I64d\n",res1,res2);            res+=(res1*res2)%mod;            res%=mod;        }        printf("%I64d\n",res);    }    return 0;}//耗时0ms

*因为须对1e9+7取模,所以可以先预处理阶乘数组fac[ ],阶乘的线性逆元inv[ ],如果每次都求一下c(n,i)的话,复杂度位O(n^2);

*另外求组合数是fac[n]/fac[m]/fac[n-m]这样的话,会爆精度,应该变除为乘,乘以所要除的数的逆元,所以先预处理一波阶乘的逆元;

*代码2比代码1高效在于预处理阶乘的线性逆元,预处理代码是学习别人的;



原创粉丝点击