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

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

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


题意: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 1000000007
const 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 1000000007
const 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高效在于预处理阶乘的线性逆元,预处理代码是学习别人的;



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值