第三十三章 数论——组合数详解(2)

news2024/11/20 0:33:47

一、组合数——卢卡斯定理

1、问题


这道题中, a , b a,b a,b的范围都是很大的,我们就无法直接用到之前所讲解的预处理阶乘的方法。

如果大家没有看过作者写的组合数(1)的话,建议大家先去看一下,今天所讲的问题需要上一篇文章的铺垫,传送门:组合数(1)

而此时就需要用到我们后文介绍的卢卡斯定理了。

2、卢卡斯定理

(1)定理内容

C a b = C a / p b / p ∗ C a   m o d ( p ) b   m o d ( p ) C_a^b=C_{a/p}^{b/p}*C_{a\ mod(p)}^{{b\ mod(p)}} Cab=Ca/pb/pCa mod(p)b mod(p)

(2)定理证明

在证明这个算法之前,我们先证明几个小的结论做铺垫。

结论1:

0 < x < p 0<x<p 0<x<p的时候:
C p x ≡ 0   m o d ( p ) C_p^x\equiv 0\ mod(p) Cpx0 mod(p)

这个很好证明
C p x = p ! x ! ( p − x ) ! = p ( p − 1 ) ! x ( x − 1 ) ! ( p − x ) ! = p x ∗ C p − 1 x − 1 C_p^x=\frac{p!}{x!(p-x)!}=\frac{p(p-1)!}{x(x-1)!(p-x)!}=\frac{p}{x}*C_{p-1}^{x-1} Cpx=x!(px)!p!=x(x1)!(px)!p(p1)!=xpCp1x1

所以 C p x C_p^x Cpx p p p的倍数。所以 C p x ≡ 0   m o d ( p ) C_p^x\equiv 0\ mod(p) Cpx0 mod(p)

结论2:

( 1 + x ) p ≡ 1 + x p ( m o d   p ) (1+x)^p\equiv1+x^p(mod\ p) (1+x)p1+xp(mod p)

证明:
根据二项展开式:

( 1 + x ) p = C p 0 x 0 + C p 1 x 1 + C p 2 x 2 + . . . + C p p x p (1+x)^p=C_p^0x^0+C_p^1x^1+C_p^2x^2+...+C_p^px^p (1+x)p=Cp0x0+Cp1x1+Cp2x2+...+Cppxp

根据我们的结论1
我们中间的项在模p的时候,都等于0。

因此,我们可以化简一下就会得到我们的结论2:

( 1 + x ) p ≡ 1 + x p ( m o d   p ) (1+x)^p\equiv1+x^p(mod\ p) (1+x)p1+xp(mod p)

卢卡斯定理证明:

a = n p + d , b = m p + d a=np+d,b=mp+d a=np+d,b=mp+d

根据二项展开式:
( 1 + x ) a ≡ ∑ i = 0 a C a i x i ( m o d   p ) (1+x)^a\equiv \sum_{i=0}^{a}C_a^ix^i(mod\ p) (1+x)ai=0aCaixi(mod p)

我们将 a = n p + d a=np+d a=np+d带入:

( 1 + x ) a (1+x)^a (1+x)a
≡ ( 1 + x ) n p + d \equiv (1+x)^{np+d} (1+x)np+d
≡ ( ( 1 + x ) p ) n ( 1 + x ) d \equiv \big((1+x)^p\big)^n(1+x)^d ((1+x)p)n(1+x)d

根据结论2,我们可以继续化简得:

≡ ( 1 + x p ) n ( 1 + x ) d \equiv \big(1+x^p\big)^n(1+x)^d (1+xp)n(1+x)d

再根据二项式展开:

≡ ∑ i = 0 n C n i x i p ∗ ∑ j = 0 d C d j x j ( m o d   p ) \equiv \sum_{i=0}^{n}C_n^ix^{ip}*\sum_{j=0}^{d}C_d^jx^{j}(mod\ p) i=0nCnixipj=0dCdjxj(mod p)

所以我们就能得到:
∑ i = 0 a C a i x i ≡ ∑ i = 0 n C n i x i p ∗ ∑ j = 0 d C d j x j ( m o d   p ) \sum_{i=0}^{a}C_a^ix^i\equiv \sum_{i=0}^{n}C_n^ix^{ip}*\sum_{j=0}^{d}C_d^jx^{j}(mod\ p) i=0aCaixii=0nCnixipj=0dCdjxj(mod p)

左式和右式中,一一对应,我们可以得到如下结果:

对于任何一项: x e x^e xe

C a b x b = C n x ∗ C d y x b C_a^bx^b=C_n^{x}*C_d^{y}x^b Cabxb=CnxCdyxb

其中: b = x ∗ p + y b=x*p+y b=xp+y并且 a = n p + d a=np+d a=np+d

即:
C a b ≡ C n x ∗ C d y C_a^b\equiv C_n^{x}*C_d^{y} CabCnxCdy

而:

x = b p n = a p x=\frac{b}{p}\\ n=\frac{a}{p} x=pbn=pa

y = b   m o d   p d = a   m o d   p y=b\ mod\ p\\ d=a\ mod\ p y=b mod pd=a mod p

我们将其带入即可得到:
C a b = C a / p b / p ∗ C a   m o d ( p ) b   m o d ( p ) C_a^b=C_{a/p}^{b/p}*C_{a\ mod(p)}^{{b\ mod(p)}} Cab=Ca/pb/pCa mod(p)b mod(p)

上述式子即我们的卢卡斯定理

3、代码

#include<iostream>
using namespace std;
typedef long long ll;
int n;
ll qmi(ll a,ll b,ll p)
{
    ll res=1;
    while(b)
    {
        if(b&1)res=res%p*a%p;
        a=a%p*a%p;
        b>>=1;
    }
    return res;
}
ll c(ll a,ll b, ll p)
{
    if(b>a)return 0;
    ll res=1;
    for(int i=1,j=a;i<=b;i++,j--)
    {
        res=res%p*j%p;
        res=res%p*qmi(i,p-2,p)%p;
    }
    return res;
}
ll lucas(ll a,ll b,ll p)
{
    if(a<p&&b<p)return c(a,b,p);
    else return c(a%p,b%p,p)*lucas(a/p,b/p,p)%p;
}
int main()
{
    cin>>n;
    while(n--)
    {
        ll a,b,p;
        cin>>a>>b>>p;
        cout<<lucas(a,b,p)<<endl;
    }
}

这里解释一下我们的中间计算 C a b C_a^b Cab的代码:

我们这里用到了另一个定义:

C a b = A a b A b b = a ( a − 1 ) . . . ( a − b + 1 ) b ( b − 1 ) ( b − 2 ) . . . 1 C_a^b=\frac{A_a^b}{A_b^b}=\frac{a(a-1)...(a-b+1)}{b(b-1)(b-2)...1} Cab=AbbAab=b(b1)(b2)...1a(a1)...(ab+1)

二、组合数——高精度+欧拉筛

1、问题:

在这里插入图片描述
这道题的关键就在于题目给出的 a a a b b b实在是太大了,肯定会爆掉。所以我们要使用高精度算法

2、思路

这道题的难点就是会爆掉,所以我们要用到最开始学习的高精度算法。然后利用定义求解。

我们先来回顾一下组合数的定义:

C n m = n ! m ! ( n − m ) ! C_n^m=\frac{n!}{m!(n-m)!} Cnm=m!(nm)!n!

如果我们直接套用高精度算法的话,肯定是相当麻烦的。

所以我们将其变形一下:

根据算数基本定理,我们能够将 C n m C_n^m Cnm写成有限个质数乘积的形式。

那么问题来了,我们如何拆解呢?

首先,拆解质数的关键,在于我们要知道可能包含哪些质数。由于我们的 n ≥ m n\geq m nm。所以我们只需要去筛选 1 − n 1-n 1n之间所有的质数即可。

而筛选质数的话,我们可以选择之前学过的埃氏筛法或者欧拉筛法。我们此处就选择欧拉筛法了。

当我们知道了包含的质数之后,我们还要计算的是 C n m C_n^m Cnm中所含质数的个数。

那么此时我们将用到下面这个结论:

n ! 中 p 的个数: s u m = n p + n p 2 + n p 3 + . . . . n!中p的个数:\\ sum=\frac{n}{p}+\frac{n}{p^2}+\frac{n}{p^3}+.... n!p的个数:sum=pn+p2n+p3n+....

为什么呢?
如果我们的阶乘中含有一个 p k p^k pk的倍数。那么此时这个数应该包含 k k k p p p

如果假设 k > 1 k>1 k>1的话,

那么他必定是 p p p的个数,此时它会被取出来一次。

同时,如果 k > 2 k>2 k>2的话,

那么它也必定是 p 2 p^2 p2的个数,此时它会被取出来一次。

那么以此类推,它就会被取出来 k k k次,也就是说它会被计算 k k k次。恰好验证了我们的公式。

当我计算出了质数和对应的指数的时候,我们只需要套用一下高精度乘法,乘在一起即可。

3、代码

#include<iostream>
#include<vector>
using namespace std;
const int N=5e3+10;
int primes[N],cnt;
int sum[N];
bool st[N];
void get_primes(int a)
{
    for(int i=2;i<=a;i++)
    {
        if(!st[i])primes[cnt++]=i;
        for(int j=0;primes[j]<=a/i;j++)
        {
            st[primes[j]*i]=true;
            if(i%primes[j]==0)break;
        }
    }
}
int get_nums(int a,int pri)
{
    int res=0;
    while(a)
    {
        res+=a/pri;
        a/=pri;
    }
    return res;
}
vector<int>mul(vector<int>a,int b)
{
    vector<int>c;
    int t=0;
    for(int i=0;i<a.size();i++)
    {
        t+=a[i]*b;
        c.push_back(t%10);
        t/=10;
    }
    while(t)
    {
        c.push_back(t%10);
        t/=10;
    }
    return c;
}

int main()
{
    int a,b;
    cin>>a>>b;
    get_primes(a);
    for(int i=0;i<cnt;i++)
    {
        int p=primes[i];
        sum[i]=get_nums(a,p)-get_nums(b,p)-get_nums(a-b,p);
    }
    
    vector<int>res;
    res.push_back(1);
    
    for(int i=0;i<cnt;i++)
    {
        for(int j=0;j<sum[i];j++)
        {
            res=mul(res,primes[i]);
        }
    }
    for(int i=res.size()-1;i>=0;i--)printf("%d",res[i]);
    puts("");
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/124665.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【小程序】微信小程序获取头像、呢称2013年最新解决方案(已解决)

一、效果 二、wxml代码 <!-- 点击头像获取头像 --> <view style"margin-bottom: 20px;text-align: center;">---- 点击头像获取logo ----</view> <view class"logo"><button style"width: 100px;height:100px;" cla…

易诚互动在创业板更新招股书:上半年出现亏损,极其依赖阿里云

12月27日&#xff0c;北京易诚互动网络技术股份有限公司&#xff08;下称“易诚互动”&#xff09;在深圳证券交易所更新招股书&#xff0c;准备在创业板上市。本次冲刺上市&#xff0c;易诚互动计划募资3.13亿元&#xff0c;将用于用于数字银行应用平台升级项目、大数据智能风…

问题解决丨对不起,小米路由器出现网络连接问题无法打开网页

之前Chrome浏览器一直用的好好的&#xff0c;不过最近出现以下问题 “对不起&#xff0c;小米路由器出现网络连接问题无法打开网页”。 奇怪的是&#xff0c;使用Edge浏览器可以正常访问网页&#xff0c;但是Chrome会不行&#xff0c;每次如果要正常访问需要关闭所有Chrome的…

迭代次数的隐约脚印

( A, B )---1*30*2---( 1, 0 )( 0, 1 ) 继续一维的实验&#xff0c;这次区别是A和B都由5个点构成。 A B 迭代次数&#xff08;7e-4&#xff09; 0 0 0 0 0 1 1 1 0 1 1b 1b 1b 0 1b 0*0*0*0*0-1*1*1*0*1 22047.98995 如A为00000&#xff0c;B为11101&#…

外贸网站优化与外贸网页优化的区别

对于大多数外贸企业和个人站长来说&#xff0c;网页优化是非常重要的&#xff0c;但是说到网站优化&#xff0c;很多人会有一个误区&#xff0c;就是经常把网站优化和搜索引擎优化混为一谈。虽然两者之间有很多重叠的地方&#xff0c;但是如果再细分的话&#xff0c;两者还是有…

JSP ssh 物流信息管理系统myeclipse开发oracle数据库MVC模式java编程计算机网页设计

一、源码特点 JSP ssh 物流信息管理系统是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用 B/S模式开发。开发环境为TOMCAT7.…

专业低代码平台应该具备什么?

编者按&#xff1a;低代码火爆的同时&#xff0c;很多低代码平台如雨后春笋般涌出&#xff0c;那么一个真正的专业低代码平台应该具备什么特性呢&#xff1f;本文将为你剖析老厂商天翎低代码平台的特点&#xff0c;一起感受低代码平台的魅力。 关键词&#xff1a;读写分析&…

[BSidesCF 2019]SVGMagic (XXE)

[BSidesCF 2019]SVGMagic 首先打开界面&#xff0c;感觉就是一个文件上传的题目 &#xff0c;然后上传了jpg/php/png/.htaccess&#xff0c;感觉不是一道简单的题目&#xff0c;但是抓包也没有什么有用的信息。 页面上的英文翻译过来就是&#xff0c;使用Magic将SVG转换为PNG&…

C#,简单易用、稳定可靠的统计学的常用算法、概要介绍与源代码

1、统计学常用算法 统计分析科学 在“政治算术”阶段出现的统计与数学的结合趋势逐渐发展形成了“统计分析科学”。 十九世纪末&#xff0c;欧洲大学开设的“国情纪要”或“政治算数”等课程名称逐渐消失&#xff0c;代之而起的是“统计分析科学”课程。当时的“统计分析科学”…

深度学习笔记:神经网络(1)

对于感知机相关内容&#xff0c;可以参考我上一篇文章&#xff1a; https://blog.csdn.net/Raine_Yang/article/details/128461600?spm1001.2014.3001.5501 在图示中&#xff0c;最左边一列为输入层&#xff0c;最右边一列为输出层&#xff0c;中间为中间层&#xff0c;也叫隐…

Android 传感器概述(一)

Android 传感器概述&#xff08;一&#xff09;Android 传感器概述&#xff08;一&#xff09;传感器简介传感器框架识别传感器和传感器特性监控传感器事件处理不同的传感器配置在运行时检测传感器使用 Google Play 过滤器定位特定的传感器配置传感器坐标系Android 传感器概述&…

Pytorch的 Dataset 的使用

此案例教我们加载并处理TorchVision的FashionMNIST Dataset。 root 目录是 train/test data 存储的地方 downloadTrue 如果root目录没有&#xff0c;则从网上下载 transform and target_transform specify the feature and label transformations import torch from torch.u…

第十五讲:神州交换机端口安全配置

知识点&#xff1a; 开启端口安全模式&#xff1b;设置端口最大安全数&#xff1b;端口绑定MAC地址&#xff1b;违规处理&#xff1b;锁定安全端口&#xff1b;MAC地址与IP的绑定&#xff1b;端口镜像。 实验拓扑如下图所示 PC机 IP地址 掩码 MAC地址 端口 PC1 192.168.…

Windows10安装ubuntu(WSL2,可直接调用Win10程序) —2022年笔记

算是wsl2的使用总结。 一。启动win10虚拟机模块 1. 打开控制面板&#xff08;或开始->运行: control&#xff09; 2. 点击最左边的 “启用或关闭windows功能”&#xff0c;会弹出模块勾选界面 3. 勾上 Hyper-V、适用于Linux的Windows子系统、虚拟机平台 4. 点击确定按钮即开…

【微服务】Nacos 注册中心的设计原理

目录 一、前言 二、数据模型 三、数据一致性 四、负载均衡 五、健康检查 六、性能与容量 七、易用性 八、集群扩展性 九、用户扩展性 十、结尾 &#x1f496; Spring家族及微服务系列文章 一、前言 服务发现是⼀个古老的话题&#xff0c;当应用开始脱离单机运行和访…

Mysql查询性能优化

Mysql查询性能优化0.前言1.为什么查询速度会慢2. 慢查询基础&#xff1a;优化数据访问2.1 是否向数据库请求了不需要的数据2.2 MYSQL是否在扫描额外的记录响应时间扫描的行数和返回的行数3. 重构查询方式3.1 一个复杂查询还是多个简单查询3.2 切分查询3.3 分解关联查询0.前言 …

DAG任务调度系统 Taier 演进之道,探究DataSourceX 模块

熟悉Taier的小伙伴们应该都知道&#xff0c;在11月7日发布的Taier1.3新版本中&#xff0c;我们融合了「DataSourceX 模块」。这是十分重要的一个变化&#xff0c;移除Taier外部插件依赖&#xff0c;新增数据源插件相关特性&#xff0c;支持后续Taier对接更多的RDBMS类型的SQL任…

小鹅通六周年:新知识服务时代,做好企业的“共享CTO”

2022年&#xff0c;产业数字化汹涌而来&#xff0c;驱动企业变革转型的同时&#xff0c;知识也以新的形式出现在各行各业。人人表达和传递知识&#xff0c;知识服务开始进入“下半场”。 如何应用数字化产品满足个人和组织的知识表达需求&#xff1f;作为知识产品与用户服务的…

HashMap的结构,1.7和1.8有哪些区别

一、真实面试题之&#xff1a;Hashmap的结构&#xff0c;1.7和1.8有哪些区别 不同点&#xff1a; &#xff08;1&#xff09;JDK1.7用的是头插法&#xff0c;而JDK1.8及之后使用的都是尾插法&#xff0c;那么他们为什么要这样做呢&#xff1f;因为JDK1.7是用单链表进行的纵向…

【开源项目】单点登录框架XXL-SSO源码解析

单点登录框架XXL-SSO源码解析 项目介绍 XXL-SSO 是一个分布式单点登录框架。只需要登录一次就可以访问所有相互信任的应用系统。 拥有"轻量级、分布式、跨域、CookieToken均支持、WebAPP均支持"等特性。现已开放源代码&#xff0c;开箱即用。 项目地址 https://g…