zoukankan      html  css  js  c++  java
  • [模板] 数学基础:快速幂/乘/逆元/exGCD/(ex)CRT/(ex)Lucas定理

    方便复制

    快速乘/幂

    时间复杂度 (O(log n)).

    ll nmod;
    //快速乘
    ll qmul(ll a,ll b){
    	ll l=a*(b>>hb)%nmod*(1ll<<hb)%nmod;
    	ll r=a*(b&((1<<hb)-1))%nmod;
    	return (l+r)%nmod;
    }
    //快速幂
    ll qpow(ll a,ll b){
    	ll res=1;
    	while(b){
    		if(b&1)res=res*a%nmod;
    		a=a*a%nmod;
    		b>>=1;
    	}
    	return res;
    }
    

    exgcd

    内容

    解不定方程 $ ax+by = c $

    时间复杂度 (O(log n)).

    void exgcd(ll a,ll b,ll& x,ll& y,ll& d){ //a&b should > 0
        b==0?(x=1,y=0,d=a):(exgcd(b,a%b,y,x,d),y-=x*(a/b));
    }
    //use
    	ll a,b,c;
    	ll m,x,y;
    	exgcd(a,b,x,y,m);
    	if(c%m!=0)cout<<"No
    "; //无解
    	else{
    		c/=m,x*=c,y*=c,a/=m,b/=m;
    //令x取最小非负整数
    		x1=x%b;if(x1<0)x1+=abs(b);
    		y1=(c-a*x1)/b;
    //令y取最小非负整数
    		y1=y%a;if(y1<0)y1+=abs(a);//(y>=0?y%a:y%a+abs(a));
    		x1=(c-b*y1)/a;
    	}
    

    逆元

    内容

    (n * x equiv 1 (mod m)) 最小正整数解.

    单个数

    时间复杂度 (O(log n)).

    //1:qp(n,nmod-2)
    //2
    ll getv(ll n){return n<0?n+nmod:n;}
    ll inv(ll n){
        ll x,y,d;
        exgcd(getv(n%nmod),p,x,y,d);
        return x%p+(x<0?p:0);
    }
    

    线性求逆元

    时间复杂度 (O(n)).

    1. 公式法(并不能记住板子)
    2. 阶乘法
      利用下面的公式:

    [(n!)^{-1} = ((n+1)!)^{-1} cdot (n+1) ]

    [n^{-1} = (n!)^{-1} cdot (n-1)! ]

    代码

    ll fac[nsz],ifac[nsz];
    void init(int bnd){
    	fac[0]=1;
    	rep(i,1,bnd)fac[i]=i*fac[i-1]%nmod;
    	ifac[bnd]=inv(fac[bnd]);
    	repdo(i,bnd-1,0){
    		ifac[i]=ifac[i+1]*(i+1)%nmod;
    	}
    }
    

    CRT

    中国剩余定理 && 扩展中国剩余定理 - niiick - CSDN博客

    内容

    解线性同余方程组 (x equiv a_i pmod{m_i}, forall i in {1, 2, cdots , n}). 其中(m_i)两两互质.

    (M=prod_{i=1}^nm_i), (M_i=frac M{m_i});
    (M_i^{-1})(M_i) 关于 (mod m_i)的逆元,

    则可以构造出通解

    [ x equiv sum_{i=1}^k a_iM_iM_i^{-1} pmod M $$. 时间复杂度 $O(n cdot 逆元)$, 通常为 $O(n log n)$. ### 代码 ``` ll crt(ll a,ll m,ll m0){//m0 | m; (m0,m/m0)=1 return m/m0*inv(m/m0,m0)%m*a%m; } ``` ## excrt 模数不互质. 利用合并的思想求解. 时间复杂度$O(n log n)$. ### 代码 ``` ll excrt(ll *a,ll *m,ll n){ ll a0=a[1],m0=m[1],x,y,g; rep(i,2,n){ g=exgcd(m0,m[i],x,y); if((a[i]-a0)%g!=0)return -1; x=(a[i]-a0)/g*x%(m[i]/g); a0+=x*m0; m0=m0/g*m[i]; a0%=m0; } return a0<0?(a0%m0+m0):(a0%m0); } ``` ## Lucas定理 [Lucas定理 - permui - 博客园](https://www.cnblogs.com/owenyu/p/6724560.html) ### 内容 求 $inom n m mod p$, 保证$p in { prime }$. 设$n=(a_0a_1dots a_k)_p$, $m=(b_0b_1dots b_k)_p$, 有 $$ inom n mequiv prod _{i=0}^kinom {a_i} {b_i} pmod p ]

    也即

    [inom n m equiv inom {lfloor frac{n}{p} floor} {lfloor frac{m}{p} floor} cdot inom {n mod p} {m mod p} pmod p ]

    递归求解.

    时间复杂度 (O(p log_p n)) , 或者 (O(p)) 预处理, (O(log_p n)) 单次询问.

    代码

    ll c(ll n,ll m){
        if(m<0||m>n)return 0;
        return fact(n)*inv(fact(m)*fact(n-m)%p)%p;
    }
    ll lucas(int n,int m){//c(n,m)%p
        return m?lucas(n/p,m/p)*c(n%p,m%p)%p:1;
    }
    

    exLucas

    【知识总结】扩展卢卡斯定理(exLucas) - Inspector_Javert - CSDN博客

    p为合数.

    分解质因数+阶乘取模+组合数+excrt

    注意如果计算 (n! mod p^k) 时如果计算 (p^x) 对结果的贡献, 将无法求逆元. 因此需要求 (frac{n!}{p^x} mod p^k), 即忽略p的幂, 然后在求组合数时再乘回来.

    码量++

    复杂度太长... 当它是 (O(p log p)) 好惹

    代码

    ll qpow(ll a,ll b,ll nmod){
        ll res=1;
        while(b){
            if(b&1)res=res*a%nmod;
            a=a*a%nmod;
            b>>=1;
        }
        return res;
    }
    
    void exgcd(ll a,ll b,ll &x,ll &y,ll &d){
    	b?(exgcd(b,a%b,y,x,d),y-=x*(a/b)):(x=1,y=0,d=a);
    }
    ll inv(ll a,ll m){
    	ll x,y,d;
    	exgcd(a,m,x,y,d);
    	return x>=0?x%m:x%m+m;
    }
    ll crt(ll a,ll m,ll m0){//m0 | m; (m0,m/m0)=1
    	return m/m0*inv(m/m0,m0)%m*a%m;
    }
    
    ll fact(ll n,ll p,ll pk){//(n!/p^x)%(p^k)
    	if(n<=1)return 1;
    	ll ans=1,tmp=n%pk;
    	rep(i,1,pk){
    		if(i%p)ans=ans*i%pk;
    	}
    	ans=qpow(ans,n/pk,pk);
    	rep(i,1,tmp){
    		if(i%p)ans=ans*i%pk;
    	}
    	return ans*fact(n/p,p,pk)%pk;
    }
    
    ll c(ll n,ll m,ll p,ll pk){//c(n,m)%(p^k)
    	ll sum=0;
    	for(ll i=n;i;i/=p)sum+=i/p;
    	for(ll i=m;i;i/=p)sum-=i/p;
    	for(ll i=n-m;i;i/=p)sum-=i/p;
    	return qpow(p,sum,pk)*fact(n,p,pk)%pk*inv(fact(m,p,pk),pk)%pk*inv(fact(n-m,p,pk),pk)%pk;
    }
    
    ll fac[40][2],pf; //0 p;  1 pk
    void getfac(ll n){
    	ll tmp=sqrt(n);
    	rep(i,2,tmp){
    		if(n%i==0){
    			fac[++pf][0]=i,fac[pf][1]=1;
    			while(n%i==0)n/=i,fac[pf][1]*=i;
    		}
    	}
    	if(n>1)fac[++pf][0]=n,fac[pf][1]=n;
    }
    ll exlucas(ll n,ll m,ll p){
    	ll ans=0;
    	getfac(p);
    	rep(i,1,pf){
    		ans=(ans+crt(c(n,m,fac[i][0],fac[i][1]),p,fac[i][1]))%p;
    	}
    	return ans;
    }
    
  • 相关阅读:
    git的突出解决--git rebase之abort、continue、skip
    servlet中service() 和doGet() 、doPost() 学习笔记
    Spring IoC容器初始化过程学习
    浅探SpringMVC中HandlerExecutionChain之handler、interceptor
    常用路由命令
    路由配置命令
    cout 计算顺序问题
    第一次作业
    记录一个微信网页授权中不小心踩到的坑(Curl请求返回false)
    善用mysql中的FROM_UNIXTIME()函数和UNIX_TIMESTAMP()函数
  • 原文地址:https://www.cnblogs.com/ubospica/p/10363574.html
Copyright © 2011-2022 走看看