组合数的几种球阀&Lucas定理 By cellur925

先来了解几个概念:排列数,组合数。

一、定义及有用的性质

排列数:从n个不同元素中依次取出m个元素排成一列的方案数。P(n,m)=n!/(n-m)!

组合数:从n个不同元素中依次取出m个元素形成一个集合的方案数。(注意,集合满足无序性,这是和排列数的区别)。C(n,m)=n!/m!(n-m)!

组合数性质 

  性质1 C(n,m)= C(n,n-m)

  性质2 C(n,m)=C(n-1,m-1)+C(n-1,m)

  性质3 C(n,0)+C(n,1)+C(n,2)+...+C(n,n)=2^n(道出组合数与杨辉三角间的联、系)

二、组合数球阀

① 递推 复杂度为n² 

  c[i][j]=c[i-1][j]+c[i-1][j-1]

但是要注意要命的初始化--

丢一段代码跑。

② 预处理阶乘+逆元 复杂度为nlogn

首先我们应该知道,除以一个数等于乘上这个数的逆元,那么我们就可以直接(生猛 地利用原始带有阶乘的公式,分母我们处理逆元。这里用到的是最简单的费马小定理方法,一个数x在膜p意义下的逆元等于x^(p-2)

丢一段代码跑。

 1 #include<cstdio>
 2 #include<algorithm>
 3 
 4 using namespace std;
 5 typedef long long ll;
 6 const int p=1e9+7;
 7 
 8 ll n,k,x;
 9 ll ans=1;
10 
11 ll ksm(ll a,ll b)
12 {
13     ll tmp=1;
14     while(b)
15     {
16         if(b&1) tmp=tmp*a%p;
17         b>>=1;
18         a=a*a%p;
19     }
20     return tmp;
21 }
22 
23 int main()
24 {
25     //求C(n,k) 
26     scanf("%d%d",&n,&k);
27     for(int i=1;i<=n;i++) ans=ans*i%p;
28     for(int i=1;i<=k;i++) ans=ans*ksm(i,p-2)%p;
29     for(int i=1;i<=n-k;i++) ans=ans*ksm(i,p-2)%p;
30     printf("%lld",ans);    
31     return 0;
32 } 
View Code

③ 分解质因数 复杂度为nlogn

前导芝士:算术分解定理。

我们把分子分母都进行分解质因数,把整个分子分母对应的质因数的指数相减(消去)

丢一段代码跑。

 1 #include<cstdio>
 2 #include<algorithm>
 3 using namespace std;
 4 typedef long long LL;
 5 int n,m;
 6 int p;
 7 int i,j;
 8 int tot;
 9 int prim[100010],cnt[100010];
10 bool flag[100010];
11 
12 void fj(int a,int k)//分解质因数 k=1/-1  
13 {//k==1时为分子操作 质因子数++
14  //k==-1时为分母操作 质因子数-- 
15     int x=a;
16     for (int i=1;i<=tot;i++)
17     {
18         if (x%prim[i]==0) 
19         {
20             while (x%prim[i]==0) x/=prim[i],cnt[prim[i]]+=k;
21         }
22         if (x==1) break;
23         if (prim[i]*prim[i]>n) break;
24     }
25     if (x>1) cnt[x]+=k;
26 }
27 
28 int Pow(int x,int a)
29 {
30     int ans=1; int j=1;
31     while (j<=a)
32     {
33         if (j&a) ans=((LL)ans*(LL)x)%p;
34         j<<=1;
35         x=((LL)x*(LL)x)%p;
36     }
37     return ans;
38 }
39 
40 int main()
41 {
42     scanf("%d%d%d",&n,&m,&p);
43     //筛素数 
44     for (i=2; i<=n; i++)
45     {
46         if (!flag[i]) prim[++tot]=i;
47         for (j=1; j<=tot; j++)
48         {
49             if (prim[j]*i>n) break;
50             flag[prim[j]*i]=1;
51             if (i%prim[j]==0) break;
52         }
53     }
54     //printf("/////");
55     int a=m; int b=(n-m);
56     int c=max(a,b);
57     //一定有一部分会自己消去(上下完全相同) 
58     for (i=c+1; i<=n; i++)
59     {//分子操作 
60         fj(i,1);
61     }
62     //分母操作 
63     for (i=1;i<=min(a,b);i++) fj(i,-1);
64     int ans=1;
65     for (i=1; i<=n; i++) 
66     {
67         if (cnt[i]>0) ans=((LL)ans*Pow(i,cnt[i]))%p;
68     }
69     printf("%d\n",ans);
70     return 0;
71  } 
View Code

三、卢卡斯定理

卢卡斯定理用于求组合数取膜的膜数为质数的情况。

(代填坑

猜你喜欢

转载自www.cnblogs.com/nopartyfoucaodong/p/9543206.html