bzoj4517: [Sdoi2016]排列计数--数学+拓展欧几里得

这道题是数学题,由题目可知,m个稳定数的取法是Cnm

然后剩下n-m本书,由于编号为i的书不能放在i位置,因此其方法数应由错排公式决定,即D(n-m)

  错排公式:D[i]=(i-1)*(D[i-1]+D[i-2]);

所以根据乘法原理,答案就是Cnm * D(n-m)

 

接下来就是怎么求组合数的问题了

由于n≤1000000,因此只能用O(n)的算法求组合,这里用乘法逆元(inv[])来辅助求组合数

即 Cnm = n! / ((n-m)! * m!) = fac[n]*inv[n-m]*inv[m]

 

那么乘法逆元是什么呢?

假设一个数a,且a关于P的乘法逆元为x

那么 ax≡1 (mod P). 当且仅当 a 与 P 互质时x有解

简单的说,就是找一个数x,使得(x*a) mod P = 1

不难得出三者符合 ax+Py=1 (裴蜀定理), y可能是负数

因此我们可以用拓展欧几里得算出x的值,即为乘法逆元(用inv保存)

 

对于求出inv的过程,我们可以不必每次暴力求拓展欧几里得,可由下列递推式O(n)求出

  inv[i]=(i+1)*inv[i+1]

 

而D数组只要O(n)推即可,其中D[0]=1, D[1]=0;

 

这道题让我明白。。组合数可以O(n)求得,了解了乘法逆元是什么,并且了解到世界上有个叫错排公式的神奇东西Orz

 

 1 #include<stdio.h>
 2 #include<algorithm>
 3 #include<string.h>
 4 #define LL long long
 5 using namespace std;
 6 const int maxn = 1000005;
 7 const LL MOD = 1e9+7;
 8 int T,n,m;
 9 LL f[maxn],inv[maxn],d[maxn];
10 
11 inline void read(int &x){
12     char c=getchar(); x=0;
13     while (c<'0' || c>'9') c=getchar();
14     while (c>='0' && c<='9') x=x*10+c-48, c=getchar();
15 }
16 
17 inline LL ex_gcd(LL &x, LL &y, LL a, LL b){
18     if (b==0){
19         x=1; y=0;
20         return a;
21     }
22     LL res=ex_gcd(x,y,b,a%b);
23     LL t=x; x=y;
24     y=t-a/b*x;
25     return res;
26 }
27 
28 inline LL calc(LL a, LL b){
29     LL x,y;
30     if (ex_gcd(x,y,a,b) == 1LL)
31         return (x+b)%b;
32 }
33 
34 int main(){
35     read(T);
36     f[0]=1;
37     for (int i=1; i<=maxn; i++) f[i]=f[i-1] * (LL)i % MOD;
38     inv[1000000]=calc(f[1000000],MOD);
39     for (int i=maxn-6; i>=0; i--) inv[i]=inv[i+1] * (LL)(i+1) % MOD;
40     d[0]=1; d[1]=0; d[2]=1;
41     for (int i=3; i<=maxn; i++) d[i]=(LL)(i-1)*(d[i-1]+d[i-2]) % MOD;
42     while (T--){
43         read(n); read(m);
44         LL ans=1LL;
45         //printf("haha %lld %lld %lld %lld\n", f[n], inv[n-m], inv[m], d[n-m]);
46         ans=ans*f[n]*inv[n-m] % MOD;
47         ans=ans*inv[m] % MOD;
48         ans=ans*d[n-m] % MOD;
49         printf("%lld\n", ans);
50     }
51     return 0;
52 }

 

posted @ 2016-04-25 21:08  mzl0707  阅读(281)  评论(0编辑  收藏  举报