Bzoj 4517: [Sdoi2016]排列計數(排列組合)

4517: [Sdoi2016]排列計數
Time Limit: 60 Sec Memory Limit: 128 MB
Description
求有多少種長度爲 n 的序列 A,知足如下條件:
1 ~ n 這 n 個數在序列中各出現了一次
若第 i 個數 A[i] 的值爲 i,則稱 i 是穩定的。序列剛好有 m 個數是穩定的
知足條件的序列可能不少,序列數對 10^9+7 取模。
Input
第一行一個數 T,表示有 T 組數據。
接下來 T 行,每行兩個整數 n、m。
T=500000,n≤1000000,m≤1000000
Output
輸出 T 行,每行一個數,表示求出的序列數
Sample Input
5
1 0
1 1
5 2
100 50
10000 5000
Sample Output
0
1
20
578028887
60695423
HINT
Source
鳴謝Menci上傳ios

/*
作法和題目同樣.
簡單的組合數學題.
答案=C(n,m)*F[n-m].
F[i]表示i個數的錯排個數.
若是忘了公式能夠用容斥原理推一發....
*/
#include<iostream>
#include<cstdio>
#define MAXN 1000001
#define LL long long
#define mod 1000000007
using namespace std;
LL n,m,ans,f[MAXN],M[MAXN];
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9') x=x*10+ch-48,ch=getchar();
    return x*f;
}
void pre()
{
    f[0]=1,f[1]=0,f[2]=1;
    for(int i=3;i<=MAXN-1;i++) f[i]=(i-1)*(f[i-1]+f[i-2])%mod;
    M[0]=1;
    for(int i=1;i<=MAXN-1;i++) M[i]=M[i-1]*i%mod; 
}
LL mi(LL a,int b)
{
    LL tot=1;
    while(b)
    {
        if(b&1) tot=tot*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return tot;
}
void slove()
{
    ans=M[n]*mi(M[m],mod-2)%mod*mi(M[n-m],mod-2)%mod*f[n-m]%mod;
    printf("%lld\n",ans);
}
int main()
{
    int t;
    t=read();pre();
    while(t--)
    {
        n=read(),m=read();
        slove();
    }
    return 0;
}
相關文章
相關標籤/搜索