P3807【模板】卢卡斯定理

题解大部分都是递归实现的,给出一种非递归的形式
话说上课老师讲的时候没给代码,然后自己些就写成了这样

对于质数\(p\)给出卢卡斯定理:
\[\tbinom{n}{m}=\tbinom{n \bmod p}{m \bmod p}\tbinom{\lfloor \frac{n}{p}\rfloor}{\lfloor \frac{m}{p} \rfloor}\pmod p\]
其实它还有另一种形式,虽然本质上没啥区别:
\[\tbinom{n}{m}=\prod_{i=1}^k \tbinom{a_i}{b_i} \pmod p\]
其中,\(a,b\)分别为\(n,m\)的\(p\)进制下的每一位,\(k\)是它们位数的较大值,当然如果有数不足\(k\)位,要补前导0



来证明一下
其实证明方法也是看了别人blog才知道的
设\(s=\lfloor \dfrac{n}{p}\rfloor,t=\lfloor \dfrac{m}{p}\rfloor\)
则有\(q,w\)使得\(n=sp+q,m=tp+w\)
再考虑一个二项式:
\[(1+x)^n=((1+x)^p)^s(1+x)^q\]
?
先由费马小定理推个结论:
\[x^p\equiv x\pmod p \Rightarrow (x^p+1)\equiv (x+1)\pmod p\]
\[(x+1)^p\equiv (x+1)\pmod p \]
所以:
\[(x+1)^p\equiv (x^p+1)\pmod p\]
?
把这个结论带进去:
\[(1+x)^n\equiv (1+x^p)^s(1+x)^q \pmod p\]
再由二项式定理把右边展开
\[(1+x)^n\equiv \sum_{i=1}^s \tbinom{s}{i}x^{pi}\cdot \sum_{j=1}^q \tbinom{q}{j}x^j\]
同样我们可以把左边展开:
\[(1+x)^n=\sum_{i=1}^{sp+q}\tbinom{sp+q}{i}x^i\]
然后我们可以发现,左右两遍都有\(x^{tp+w}\)次项(当然,这是在\(m\leq n\)的情形下,如果\(m>n\)结果就是0,不用考虑了)
比较一下它们的系数
左边:\(\tbinom{sp+q}{tp+w}x^{tp+w}\)
右边:\(\tbinom{s}{t}x^{tp}\cdot \tbinom{q}{w}x^w\)
这边要说明一下,不会出现别的次数组合,比如\((t-1)p\)和\((w+p)\),因为\(w,q<p\)
所以:\(\tbinom{sp+q}{tp+w}\equiv \tbinom{s}{t}\tbinom{q}{w}\pmod p\)
即:
\[\tbinom{n}{m}\equiv\tbinom{n \bmod p}{m \bmod p}\tbinom{\lfloor \frac{n}{p}\rfloor}{\lfloor \frac{m}{p} \rfloor}\pmod p\]
然后把\(\tbinom{\lfloor \frac{n}{p}\rfloor}{\lfloor \frac{m}{p} \rfloor}\)这一项不断展开,其实就变为了那种非递归形式



好了,我们终于得到了这个定理
那写代码就简单了,将\(n,m\)转化为\(p\)进制
预处理出阶乘数组,和阶乘的逆元数组
然后对于这\(p\)进制的每一位直接套组合数公式就行了
然而代码似乎没有递归的好写
另外一共5个点,我错了三遍下载了三个数据来调
那么说一下踩得坑,首先主函数for循环里的特判一定要有,避免出现数组下标变成负数,或者使用0的逆元的情况
还有多测时前导0的位置一定要清零

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#include<stack>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
    int x=0,y=1;
    char c=std::getchar();
    while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
    while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
    return y?x:-x;
}
int n,m,p;
LL fac[200006],g[200006];
int a[100006],b[100006];
inline LL power(LL x,LL y){
    reg LL ans=1;
    while(y){
        if(y&1) ans=(1ll*ans*x)%p;
        y>>=1;x=(x*x*1ll)%p;
    }
    return ans;
}
std::stack<int>s;
inline void pre(){//预处理函数
    a[0]=b[0]=0;
    while(n){
        s.push(n%p);n/=p;
    }
    while(!s.empty()) a[++a[0]]=s.top(),s.pop();
    int tmp=0;
    while(m){
        tmp++;
        s.push(m%p);m/=p;
    }
    while(b[0]+tmp<a[0]) b[++b[0]]=0;//前导零的位置一定要清零
    while(!s.empty()) b[++b[0]]=s.top(),s.pop();
    fac[0]=1;
    for(reg int i=1;i<p;i++) fac[i]=(1ll*fac[i-1]*i)%p;
    g[p-1]=power(fac[p-1],p-2);
    for(reg int i=p-2;i;i--) g[i]=(1ll*g[i+1]*(i+1))%p;
}
int main(){int t=read();while(t--){
    n=read();m=read();p=read();
    n+=m;m=n-m;
//  std::memset(a,0,sizeof a);std::memset(b,0,sizeof b);
    pre();
//      for(reg int i=1;i<=a[0];i++) std::printf("%d ",a[i]);EN;
//      for(reg int i=1;i<=b[0];i++) std::printf("%d ",b[i]);EN;
//      for(reg int i=0;i<p;i++) std::printf("%d ",fac[i]);EN;
//      for(reg int i=0;i<p;i++) std::printf("%d ",g[i]);EN;
    LL ans=1;
//      std::printf("%d %d\n",a[0],b[0]);
    for(reg int i=1;i<=a[0];i++){
        if(!b[i]) continue;
        if(a[i]<b[i]){ans=0;break;}
        if(a[i]==b[i]) continue;
        ans=(1ll*ans*fac[a[i]])%p;
        ans=(1ll*ans*g[b[i]])%p;
        ans=(1ll*ans*g[a[i]-b[i]])%p;
    }
    std::printf("%lld\n",ans);
}
    return 0;
}

原文地址:https://www.cnblogs.com/suxxsfe/p/12527265.html

时间: 2024-11-12 18:00:35

P3807【模板】卢卡斯定理的相关文章

洛谷.3807.[模板]卢卡斯定理(Lucas)

题目链接 Lucas定理 日常水题...sublime和C++字体死活不同步怎么办... //想错int范围了...不要被longlong坑 //这个范围现算阶乘比预处理快得多 #include <cstdio> typedef long long LL; const int N=1e5+5; LL n,m,p;//,fac[N+3]; LL FP(LL x,LL k,LL p) { LL t=1; for(; k; k>>=1,x=x*x%p) if(k&1) t=t*x

模板 - 卢卡斯定理

const int MOD = 10007; ll n, m; ll qpow(ll x, ll n) { ll res = 1; while(n) { if(n & 1) res = res * x % MOD; x = x * x % MOD; n >>= 1; } return res; } ll C(ll n, ll m) { if(m > n) return 0; ll up = 1, down = 1; for(ll i = n - m + 1; i <= n;

洛谷——P3807 【模板】卢卡斯定理

P3807 [模板]卢卡斯定理 题目背景 这是一道模板题. 题目描述 给定n,m,p(1\le n,m,p\le 10^51≤n,m,p≤10?5??) 求 C_{n+m}^{m}\ mod\ pC?n+m?m?? mod p C表示组合数. 一个测试点内包含多组数据. 输入输出格式 输入格式: 第一行一个整数T(T\le 10T≤10),表示数据组数 第二行开始共T行,每行三个数n m p,意义如上 输出格式: 共T行,每行一个整数表示答案. 输入输出样例 输入样例#1: 2 1 2 5 2

P3807 【模板】卢卡斯定理

P3807 [模板]卢卡斯定理 求 \(C_{m + n}^{m} \% p\) ( \(1\le n,m,p\le 10^5\) ) 错误日志: 数组开小(洼地hi阿偶我姑父阿贺佛奥UFO爱我帮你) Pre 好的我们继续恶补数学 首先复习一下 \(O(N)\) 求质数逆元的方法\[inv[1] = 1\]\[inv[i] = (p - p / i) * inv[p \% i] \% p (i >= 2)\] LL inv[maxn]; void get_inv(LL n){ inv[1] =

洛谷 P3807 【模板】卢卡斯定理

题目背景 这是一道模板题. 题目描述 给定n,m,p(1\le n,m,p\le 10^51≤n,m,p≤105) 求 C_{n+m}^{m}\ mod\ pCn+mm? mod p 保证P为prime C表示组合数. 一个测试点内包含多组数据. 输入输出格式 输入格式: 第一行一个整数T(T\le 10T≤10),表示数据组数 第二行开始共T行,每行三个数n m p,意义如上 输出格式: 共T行,每行一个整数表示答案. 输入输出样例 输入样例#1: 复制 2 1 2 5 2 1 5 输出样例#

【刷题】洛谷 P3807 【模板】卢卡斯定理

题目背景 这是一道模板题. 题目描述 给定\(n,m,p( 1\le n,m,p\le 10^5)\) 求 \(C_{n+m}^{m}\ mod\ p\) 保证 \(p\) 为prime \(C\) 表示组合数. 一个测试点内包含多组数据. 输入输出格式 输入格式: 第一行一个整数 \(T( T\le 10 )\),表示数据组数 第二行开始共 \(T\) 行,每行三个数 \(n,m,p\),意义如上 输出格式: 共T行,每行一个整数表示答案. 输入输出样例 输入样例#1: 2 1 2 5 2 1

卢卡斯定理的模板以及应用

定义: Lucas定理是用来求 C(n,m) MOD p,p为素数的值.Lucas定理:我们令n=sp+q,m=tp+r.(q,r≤p) 那么:(在编程时你只要继续对 调用 Lucas 定理即可.代码可以递归的去完成这个过程,其中递归终点为 t=0 :时间复杂度 O(logp(n)?p):) 主要解决当 n,m 比较大的时候,而 p 比较小的时候 <1e6 ,那么我们就可以借助 卢卡斯定理来解决这个问题: 模板: #include <iostream> #include <cstd

卢卡斯定理

卢卡斯定理:解决一类组合数取模问题 A.B是非负整数,p是质数.AB写成p进制:A=a[n]a[n-1]...a[0],B=b[n]b[n-1]...b[0]. 则组合数C(A,B)与C(a[n],b[n])*C(a[n-1],b[n-1])*...*C(a[0],b[0])  modp同余 即:Lucas(n,m,p)=c(n%p,m%p)*Lucas(n/p,m/p,p) 这个是单独处理n!的情况,当然C(n,m)就是n!/(m!*(n-m)!),每一个阶乘都用上面的方法处理的话,就是Luc

【知识总结】扩展卢卡斯定理(exLucas)

扩展卢卡斯定理用于求如下式子(其中\(p\)不一定是质数): \[C_n^m\ mod\ p\] 我们将这个问题由总体到局部地分为三个层次解决. 层次一:原问题 首先对\(p\)进行质因数分解: \[p=\prod_i p_i^{k_i} \] 显然\(p_i^{k_i}\)是两两互质的,所以如果分别求出\(C_n^m\ mod\ p_i^{k_i}\),就可以构造出若干个形如\(C_n^m=a_i\ mod\ p_i^{k_i}\)的方程,然后用中国剩余定理即可求解. 层次二:组合数模质数幂