Lucas定理及组合数取模

引入:

组合数C(m,n)表示在m个不同的元素中取出n个元素(不要求有序),产生的方案数。定义式:C(m,n)=m!/(n!*(m-n)!)(并不会使用LaTex QAQ)。

根据题目中对组合数的需要,有不同的计算方法。

(1)在模k的意义下求出C(i,j)(1≤j≤i≤n)共n(数量级)个组合数:

运用一个数学上的组合恒等式(OI中称之为杨辉三角):C(m,n)=C(m-1,n-1)+C(m-1,n)。

证明:

1.直接将组合数化为定义式暴力通分再合并。过程略。

2.运用组合数的含义:设m个元素中存在一个“特殊”元素a,对从m个元素中选出n个元素进行分类讨论。

第一种情况:n个元素中含有元素a,则只需在剩余m-1个元素中选出n-1个元素即可。方案数为C(m-1,n-1)。

第二种情况:n个元素中不含元素a,则只需在剩余m-1个元素中选出n个元素即可。方案数为C(m-1,n)。

这样我们就得到了一个与组合数有关的递推式,初始化C(i,0)=1,随后通过递推以O(n2)的复杂度完成计算。均摊O(1)。

例题:NOIP2016 D2T1 组合数问题 题目链接

题意:给定一个数k,然后给出t组m,n,对于每一组数据,询问对于C(i,j)(0≤i≤n,0≤j≤min(i,m)),有多少个C(i,j)是k的倍数。

数据范围:k≤21,m,n≤2000,t≤10000。子任务见题目链接。

题解:

70分做法:O(20002)预处理出所有组合数,然后每次暴力扫描C(i,j)判断是否是k的倍数。然后机智地忘记取模(没错就是我233)

90分做法:在原有70分做法的预处理中加上取模,暴力扫描判断是否为0。

100分做法:发现每次只是数据范围改变,k和组合数都没有改变,所以尝试优化重复操作。

预处理+取模后,问题变为在整张组合数表中某个范围内0的个数。我们将非0数置0,将0置1,问题转化为矩阵和。用前缀和预处理可以做到O(1)查询。

代码:(将近一年前写的 巨丑)

#include<bits/stdc++.h>
using namespace std;
const int maxn=2000+10;
int c[maxn][maxn],d[maxn][maxn],s[maxn][maxn];
int t,n,m,k;
int main()
{
   int i,j;
   cin>>t>>k;
   for(i=0;i<maxn;i++){c[i][0]=c[i][i]=1;}
   for(i=1;i<maxn;i++){for(j=1;j<i;j++){c[i][j]=(c[i-1][j-1]+c[i-1][j])%k;}}
   for(i=0;i<maxn;i++){for(j=i+1;j<maxn;j++){c[i][j]=1;}}
   for(i=0;i<maxn;i++){for(j=0;j<maxn;j++){if(c[i][j]){d[i][j]=0;}else{d[i][j]=1;}}}
   for(i=0;i<maxn;i++){s[i][0]=s[i-1][0]+d[i][0];s[0][i]=s[0][i-1]+d[0][i];}
   for(i=1;i<maxn;i++){for(j=1;j<maxn;j++){s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+d[i][j];}}
   while(t--)
   {
      int ans=0;
      scanf("%d%d",&n,&m);
      //for(i=0;i<=n;i++){for(j=0;j<=min(i,m);j++){if(!c[i][j]){ans++;}}}
      //cout<<ans<<endl;
      printf("%d\n",s[n][m]);
   }
   return 0;
}

时间: 2024-10-16 06:49:31

Lucas定理及组合数取模的相关文章

Lucas定理--大组合数取模 学习笔记

维基百科:https://en.wikipedia.org/wiki/Lucas%27_theorem?setlang=zh 参考:http://blog.csdn.net/pi9nc/article/details/9615359 http://hi.baidu.com/lq731371663/item/d7261b0b26e974faa010340f http://hi.baidu.com/j_mat/item/8e3a891c258c4fe9dceecaba 综合以上参考,我做的一下总结:

[ACM] hdu 3037 Saving Beans (Lucas定理,组合数取模)

Saving Beans Problem Description Although winter is far away, squirrels have to work day and night to save beans. They need plenty of food to get through those long cold days. After some time the squirrel family thinks that they have to solve a probl

Uva12034 (组合数取模)

题意:两匹马比赛有三种比赛结果,n匹马比赛的所有可能结果总数 解法: 设答案是f[n],则假设第一名有i个人,有C(n,i)种可能,接下来还有f(n-i)种可能性,因此答案为 ΣC(n,i)f(n-i) 另外这里给出两个求组合数的模板,卢卡斯定理的p是模数,并且要求是素数,第二个是递推式,适合于n<2000的情况 1 #include<cstdio> 2 using namespace std; 3 const int maxn = 1e3; 4 const int mod = 1005

lucas定理计算组合数

lucas定理计算组合数 C(n,k)有多种解法,1,dp递推:2,直接计算:3,lucas定理 lucas定理适合组合数取余数的计算,n和k的范围可到10^18 1 #include<iostream> 2 #include<cstdio> 3 #include<cstdlib> 4 #include<cstring> 5 #include<algorithm> 6 7 using namespace std; 8 9 const int ma

大组合数取模之lucas定理模板,1&lt;=n&lt;=m&lt;=1e9,1&lt;p&lt;=1e6,p必须为素数

typedef long long ll; /********************************** 大组合数取模之lucas定理模板,1<=n<=m<=1e9,1<p<=1e6,p必须为素数 输入:C(n,m)%p 调用lucas(n,m,p) 复杂度:min(m,p)*log(m) ***********************************/ //ax + by = gcd(a,b) //传入固定值a,b.放回 d=gcd(a,b), x , y

组合数取模Lucas定理及快速幂取模

组合数取模就是求的值,根据,和的取值范围不同,采取的方法也不一样. 下面,我们来看常见的两种取值情况(m.n在64位整数型范围内) (1)  , 此时较简单,在O(n2)可承受的情况下组合数的计算可以直接用杨辉三角递推,边做加法边取模. (2) ,   ,并且是素数 本文针对该取值范围较大又不太大的情况(2)进行讨论. 这个问题可以使用Lucas定理,定理描述: 其中 这样将组合数的求解分解为小问题的乘积,下面考虑计算C(ni, mi) %p. 已知C(n, m) mod p = n!/(m!(

hdu 3037 Saving Beans 组合数取模模板题。。

Saving Beans Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 2707    Accepted Submission(s): 1014 Problem Description Although winter is far away, squirrels have to work day and night to save b

西电校赛网络赛J题 lucas定理计算组合数

西电校赛网络赛J题  lucas定理计算组合数 问题 J: 找规律II 时间限制: 1 Sec  内存限制: 128 MB 提交: 96  解决: 16 [提交][状态][讨论版] 题目描述 现有数阵如下: 1    2  3   4     5    6 1   3   6  10  15 1   4  10   20 1   5   15 1    6 1 求这个数阵的第n行m列是多少(行列标号从1开始) 结果对10007取模 输入 多组数据,每组数据一行,包含两个整数n,m(1<=n<=

排列组合+组合数取模 HDU 5894

1 // 排列组合+组合数取模 HDU 5894 2 // 题意:n个座位不同,m个人去坐(人是一样的),每个人之间至少相隔k个座位问方案数 3 // 思路: 4 // 定好m个人 相邻人之间k个座位 剩下就剩n-(m+1)*k个座位 5 // 剩下座位去插m个不同的盒子==就等价n个相同的球放m个不同的盒子 6 // 然后组合数出来了 7 // 乘n的话是枚举座位,除m是去掉枚举第一个座位的时候,剩下人相邻的座位相对不变的情况 8 9 #include <iostream> 10 #incl