zoukankan      html  css  js  c++  java
  • 扩展卢卡斯学习笔记

    前言

    不知道为什么对这种名字前面带个“扩展”的算法一直抱有一种畏惧心理。。。

    扩展卢卡斯的作用就是在不保证(p)是质数,求(C_n^m\% p)

    但由于复杂度还是对(p)有一定依赖,因此还是做不到任意模数。

    质因数分解+(CRT)合并

    考虑我们给(p)做一个质因数分解,拆成若干(p_i^{k_i})乘积的形式。

    由于这些(p_i^{k_i})两两互质,以它们计算出的答案可以直接(CRT)合并。

    那么现在问题就被转化成了如何求解(C_n^m\%p_i^{k_i})

    (p_i^{k_i})意义下的阶乘

    首先我们考虑(C_n^m=frac{n!}{m!(n-m)!}),因此实际上只要分别求出(n!,m!,(n-m)!)(x imes p^y)的形式,那么(C_n^m)也就可以表示成(x imes p^y)的形式,就很容易求出(C_n^m\%p^k)的值了。

    要求(p^y)是很简单的,众所周知这就是(sum_{i}lfloorfrac n{p^i} floor)

    核心还是在于求(x),即(n!)除去(p)之后剩余的部分。

    显然​(n)以内所有的(k imes p)都是含(p)的特殊项,把它们拎出来全都除以(p)便得到了(lfloorfrac np floor!),那么直接递归处理就好了。

    对于剩余部分,由于模(p^k)意义下的值存在一个(p^k)的周期,因此我们暴力计算([1,p^k))中不含(p)的一般项乘积,计算(lfloorfrac n{p^k} floor)次只需给它做个快速幂,最后剩余一段不完整的直接暴力计算([1,n\%p^k))中不含(p)的一般项乘积即可。

    代码:(O(sum p_i^{k_i}logn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define LL long long
    using namespace std;
    LL n,m;int p;
    namespace ExLucas//扩展卢卡斯
    {
    	I int QP(RI x,LL y,CI X) {RI t=1;W(y) y&1&&(t=1LL*t*x%X),x=1LL*x*x%X,y>>=1;return t;}
    	I void exgcd(CI x,CI y,int& a,int& b) {y?(exgcd(y,x%y,b,a),b-=x/y*a):(a=1,b=0);}
    	I int Inv(CI x,CI X) {RI a,b;return exgcd(x,X,a,b),(a%X+X)%X;}//非质数模数求逆元
    	I int BF(CI n,CI p,CI X) {RI t=1;for(RI i=1;i<=n;++i) i%p&&(t=1LL*t*i%X);return t;}//暴力求不含p的一般项乘积
    	I int Fac(Con LL& n,CI p,CI X)//递归求阶乘(除去p)
    	{
    		return n?(1LL*QP(BF(X-1,p,X),n/X,X)*BF(n%X,p,X)%X*Fac(n/p,p,X)%X):1;//利用周期性计算一般项,递归计算特殊项
    	}
    	I LL Get(LL n,CI p) {LL t=0;W(n) t+=(n/=p);return t;}//求出p的指数
    	I int Calc(Con LL& n,Con LL& m,CI p,CI k)//计算C(n,m)%(p^k)
    	{
    		LL w=Get(n,p)-Get(m,p)-Get(n-m,p);if(w>=k) return 0;RI X=QP(p,k-w,1e9);//计算组合数中p的指数,剩余部分模数为p^(k-w)
    		return 1LL*Fac(n,p,X)*Inv(1LL*Fac(m,p,X)*Fac(n-m,p,X)%X,X)%X*QP(p,w,1e9);//计算组合数剩余部分的值,再乘上p^w
    	}
    	I void CRT(int& r1,int& p1,CI r2,CI p2)//中国剩余定理合并
    	{
    		RI k=1LL*((r2-r1)%p2+p2)*Inv(p1,p2)%p2;r1=k*p1+r1,p1*=p2;
    	}
    	I int C(Con LL& n,Con LL& m,RI p)
    	{
    		RI i,x,k,t=0,X=1;for(i=2;i*i<=p;++i) if(!(p%i))//枚举质因子p
    			{x=1,k=0;W(!(p%i)) p/=i,x*=i,++k;CRT(t,X,Calc(n,m,i,k),x);}//求解p^k与原有答案合并
    		return p^1&&(CRT(t,X,Calc(n,m,p,1),p),0),t;//可能残余一个大质因子
    	}
    }
    int main()
    {
    	return scanf("%lld%lld%d",&n,&m,&p),printf("%d
    ",ExLucas::C(n,m,p)),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    01:oracle sql developer配置
    删除eclipse或者MyEclipse的workspace记录
    c++特殊函数
    java类和对象的基础(笔记)
    java打印日历
    10_9 java笔记
    程序流程
    学习疑惑……
    位运算和逻辑运算
    多种类型的指针
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/ExLucas.html
Copyright © 2011-2022 走看看