【组合数求模】 转自AekdyCoin

【组合数求模】 转自AekdyCoin大家都在中学阶段学习了组合数的定义:这个表示的是从n个元素中选取m个元素的方案数。(PS.组合数求模似乎只用在信息学竞赛和ACM竞赛等计算机编程设计大赛中……,求在现实中的运用) 可以知道当n,m 取得比较大的时候,组合数可能很大很大(天文数字?无法度量?)例如C(100,50)=100891344545564193334812497256, 于是计算机的64

大家好,又见面了,我是你们的朋友全栈君。

大家都在中学阶段学习了组合数的定义:

【组合数求模】 转自AekdyCoin

这个表示的是从n个元素中选取m个元素的方案数。

(PS.组合数求模似乎只用在信息学竞赛和 ACM竞赛等计算机编程设计大赛中……,求在现实中的运用)

 

可以知道当n,m 取得比较大的时候,组合数可能很大很大 (天文数字?无法度量?)

例如 C(100, 50) = 100891344545564193334812497256, 于是计算机的 64位整数型已经没法阻止它了!C(1000000000, 500000000) ? C( 2^50000, 2^49999 ) ? (Note:这里^表示次方,你能计算得到2的50000次级别的组合数么?它有多少位?)

看起来似乎高精度神马的都无法阻止这个邪恶的函数的急速扩张了……

庆幸的是,在竞赛中我们能够遇到的规模也就只有10^9级别(显然是mod上某个数字K,否则输出的文件那叫一个大啊……),这是多么的小呀呀呀呀!(Note: 相比较2^50000 -_-)

一.           入门篇:我会暴力!

(1)  K = 1: 今天你学数论了么? 难度系数: 0

(2)  (K> 1) n, m <= 1000 (n * n 是可以接受的) 难度系数: 1

递推!

   c(n,m) =c(n – 1,m) + c(n – 1, m – 1)

某人: 555555 这个公式太复杂, 记忆不能!

c(5,2) = 10 = c(4,2) + c(4,1) = 6 + 4 ……

我们知道mod操作满足加法性质,即

(a + b) mod c = ( (a mod c) + (b mod c) ) mod c

c(n,m) = ( c(n – 1,m) + c(n – 1, m – 1) ) mod K

证明利用模的定义即可……很简单的

于是如此,我们只需要简单的开上一个 f[ N ][ N ],2个循环搞定!

其实我们遇到的大部分情况需要的 组合数 都可以用这个来搞定~

这里唯一可能被邪恶的其实是 K + K 溢出! 所以如果某个邪恶的题目出到 K = 2*10^9,在某些倒霉的场合会出现2个接近K的int相加,那么就溢出了!不要忘记用unsigned int… (我从来没出过这种题的!真的!)

(3)   n 巨大(10^9 级别), m巨小(10^4级别), k 很小,大约10^9

a)     m<= 1: 今天你学数论了么? 难度系数: 0

b)     m<= 10000     难度系数: 2

 【组合数求模】 转自AekdyCoin

可以发现分子分母的项数都少到可以接受!于是我们可以采取各种方式来通过:

i)                   对于每个数字,分解素因子,合并,二分求幂! (你会数论!)

ii)                 对于每个数字,只分解包含于K的素因子,例如K里面有一个素因子3,那么分解的时候我只考虑3呀,因为其他部分显然与3互质……最后统计3的次数即可……

例子:

计算C(10, 3) mod 36

C(10, 3) = (10 * 9 * 8) / (1 * 2 * 3)

对于分母:

1 : ok 逆元(有区别么?)

2: 没法逆元, (2, 36) = 2

3: 没法逆元, (3,36) = 3

为了神马啊!! 还不让人逆元啊!显然是因为邪恶的2和3,如果他们不存在,那么多么美好呀!

于是我开2个变量,记录2,3的次数

对于分子:

10: 里面只有1个2,去掉了2,剩下的部分是 10 / 2 = 5. 

9: 里面只有2个3,去掉去掉, 剩下的是 9 / (3^2) = 1.

8: 里面只有3个2,去掉去掉,剩下的是 8 / (2^3) = 1

于是啊,分母我们把剩下的部分乘起来,得到了神马?得到了 和 2,3 因子完全无关的 部分mod 36的值!就是 5 * 1 * 1 = 5了。

接下来,还有分母呢

1: 逆元(其实你可以无视它)

2: 一个2,去掉去掉, 剩下1, 逆元继续是1(继续无视)

3: 一个3,同上

接下来发现,2有几个? 分子有4个,分母1个,所以一共只有4 – 1 = 3个

3有几个? 同上的做法,显然只有1个。

于是呢答案就是:

5 * 1 * 2^3 * 3^1 = 12( mod 36)

解释:

5 -> 分子除了因子2,3的积

1 ->分母除了因子2,3 的逆元的积

2^3 -> 最终统计发现有3个2

3^1 ->最终统计发现有1个3

请好好理解本例子,你会发现这个问题是如此的美妙!

经典例题:

http://acm.fzu.edu.cn/problem.php?pid=2020 

c)     m<= n 别想了!我不会!你会了教我!难度系数: -1

二.           基础篇:我会数论!

1)     n,m<= 10^6, K是10^9级别

对于n! 分解素因子,这里就不说了,可以参考各种帖子。

之后保存个数,二分求幂啊啊啊啊啊

2)     n,m<= 10^10, k是素数,并且K 很小(比如几百?)

其实遇到这种情况我都用一个叫Lucas定理的东西。

 【组合数求模】 转自AekdyCoin

ni,mi 就是把 n,m分解p进制的第i位的值。

例如:

                     计算 C(12, 4) mod 7

                     n = 12  (15)(base_7)

                     m = 4     (4) (base_7)

                     为了对齐,我们前面的部分补0

                     m = 4   (04) (base_7)

                     于是

                     Ans = C(5,4) * C(1,0) mod 7= 5 (mod 7)

                     有人又要问了, 如果mi > ni 怎么办呀?

                     直接为0!!!!!!!!!

              这里不给出证明,证明可以搜索到。同时由于这个应用的区域比较狭窄,显然有更简单,更好理解的算法,于是这里被无视了。

三.           究极篇

n,m <= 10^9, p <= 10^5

是不是怎么看怎么不可做呢?

第一次见到这种题目是不是觉得作者NC了,出个不可做题 >_<

第一次交发现一坨人全部WA,是不是觉得作者的数据搞疵?!!!!

 

首先要知道,这题其实等价是求:

 【组合数求模】 转自AekdyCoin

求完直接合并一个模方程即可。(CRT)

p^c 的规模大约是10^5。

c 不是1,lucas阻止不了它。

n,m太大,因子分解也阻止不了它。

下面介绍我的做法:

假设 p = 3, c = 2,也就是mod 9

 

假设n = 19

n! = 1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * …… * 19

要是可以快速得到 n! 中除掉3 以后 mod 9的结果,那么多好呀!

看3多讨厌,直接砍

type cal( int n) :

n! = [ 1 * 2 * 4 * 5 * 7 * 8 * …  * 16 * 17 * 19 ] * (3 * 6 * 9 * 12 * 15 * 18)= [ 1 * 2 * 4 * 5 * 7 * 8 * …  * 16 * 17* 19 ] * 3^6( 1 * 2 * 3 * 4 * 5 * 6)

然后发现后面的一坨实际上是 cal( n / p) !!!!

再看前半部分,尼玛是以 p^c 为周期的啊!!!

[1 * 2 * 4 * 5 * 7 * 8 ] = [10 * 11 * 13 * 14 * 16 *17 ] = (mod 9)

于是说白了,对于前面的部分,由于周期,都是浮云了

下面是 孤立出来的19

可以知道孤立出来的 长度 不超过 p^c ,于是暴力啊,暴力啊!

于是完美解决n! 中和 p无关的项 mod p^c的值!!!

接下来是分母部分,一模一样,无非多了一个求逆元(因为都和p没关系了,逆元必然存在)

我们来分析一下,这样的复杂度是如何的呢

每次递归,规模变为原来的 1/p

logp N的啊!!!

当然是层数= =

于是问题完美解决!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/163369.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • Java服务器接收上传的文件

    Java服务器接收上传的文件有时候我们服务器需要接收来自用户上传过来的文件,这时候就需要服务器端有相应的服务能够接收这个文件下面写一个简单的服务器端代码,需要的朋友可以参考一下注释很全就不多啰嗦了packagecom.SM_test.saomiao.constroller;importjava.io.File;importjava.io.FileOutputStream;importjav

  • 组合数公式(代码实现)[通俗易懂]

    组合数公式(代码实现)[通俗易懂]比如求CnmC_{n}^{m}Cnm​,代码如下:llcombi(intn,intm){//求组合数的函数llcnt=1;for(inti=0;i<m;i++){cnt*=n-i;cnt/=i+1;}returncnt;}…

  • 详解Cisco ACS AAA认证

    详解Cisco ACS AAA认证详解CiscoACSAAA认证近来,有些同学会问到关于AAA认证的问题,以及ciscoACS如何使用,那么今天我们就主要来讲一下关于这方面的知识。AAA代表Authentication、Authorization、Accounting,意为认证、授权、记帐,其主要目的是管理哪些用户可以访问服务器,具有访问权的用户可以得到哪些服务,如何…

  • 怎么新建pytest的ini文件_python读取ini配置文件

    怎么新建pytest的ini文件_python读取ini配置文件前言pytest配置文件可以改变pytest的运行方式,它是一个固定的文件pytest.ini文件,读取配置信息,按指定的方式去运行查看pytest.ini的配置选项pytest-h找到以下

  • k8s实战系列: 1-再谈为什么需要Kubernetes[通俗易懂]

    k8s实战系列: 1-再谈为什么需要Kubernetes[通俗易懂]k8s系列:再谈为什么需要Kubernetes容器解决了什么?又遇到了什么问题容器,到底是怎么一回事儿?在Docker出现之前,最为流行的是PaaS项目。PaaS项目被大家接纳的一个主要原因,就是它提供了一种名叫“应用托管”的能力。像CloudFoundry这样的PaaS项目,最核心的组件就是一套应用的打包和分发机制。更好地模拟本地服务器环境,能带来更好的“上云”体验。CloudFoundry会调用操作系统的Cgroups和Namespace机制为每一个应用单独创建一

  • STL源代码分析——STL算法remove删除算法

    STL源代码分析——STL算法remove删除算法

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号