zoukankan      html  css  js  c++  java
  • 逆元入门

    逆元(inv)

    1.什么是逆元

    当求解公式:(a/b)%m 时,因b可能会过大,会出现爆精度的情况,所以需变除法为乘法:

    设c是b的逆元,则有b*c≡1(mod m);

    则(a/b)%m = (a/b)*1%m = (a/b)*b*c%m = a*c(mod m);

    即a/b的模等于a*b的逆元的模;

    逆元就是这样应用的;

     

    2.求逆元的方法

    (1).费马小定理

    是素数的情况下,对任意整数都有。 
    如果无法被整除,则有。 
    可以在为素数的情况下求出一个数的逆元,即为逆元。

    题目中的数据范围1<=x<=10^9,p=1000000007,p是素数;

    所以x肯定就无法被p整除啊,所以最后就得出x^(p-2)为x的逆元啦。

    复杂度O(logn);

    代码:

     

    [cpp] view plain copy
     
    1. const int mod = 1000000009;  
    2. long long quickpow(long long a, long long b) {  
    3.     if (b < 0) return 0;  
    4.     long long ret = 1;  
    5.     a %= mod;  
    6.     while(b) {  
    7.         if (b & 1) ret = (ret * a) % mod;  
    8.         b >>= 1;  
    9.         a = (a * a) % mod;  
    10.     }  
    11.     return ret;  
    12. }  
    13. long long inv(long long a) {  
    14.     return quickpow(a, mod - 2);  
    15. }  


    (2)扩展欧几里得算法求逆元

     

    扩展欧几里得算法可以参考小白书;

    百度百科-乘法逆元中有这样一个例子:

     

    例如:4关于1模7的乘法逆元为多少?
    4X≡1 mod 7
    这个方程等价于求一个X和K,满足
    4X=7K+1
    其中X和K都是整数。

    求x,k就是扩展欧几里得算法了吧~

     

    可扩展欧几里得求逆元ax≡1(mod n)其中a,n互质;

    复杂度:O(logn);

    代码:

     

    [cpp] view plain copy
     
    1. ll extend_gcd(ll a, ll b, ll &x, ll &y) {  
    2.     if (b == 0) {  
    3.         x = 1, y = 0;  
    4.         return a;  
    5.     }  
    6.     else {  
    7.         ll r = extend_gcd(b, a % b, y, x);  
    8.         y -= x * (a / b);  
    9.         return r;  
    10.     }  
    11. }  
    12. ll inv(ll a, ll n) {  
    13.     ll x, y;  
    14.     extend_gcd(a, n, x, y);  
    15.     x = (x % n + n) % n;  
    16.     return x;  
    17. }  

     

     

    (3) 逆元线性筛 ( P为质数 )

    求1,2,...,N关于P的逆元(P为质数)

    复杂度:O(N)

    代码:

     

    [cpp] view plain copy
     
    1. const int mod = 1000000009;  
    2. const int maxn = 10005;  
    3. int inv[maxn];  
    4. inv[1] = 1;  
    5. for(int i = 2; i < 10000; i++)  
    6.     inv[i] = inv[mod % i] * (mod - mod / i) % mod;  

    如果是求阶乘的逆元呢?(阶乘数组:fac[ ])

    代码:

    1. for(ll i=maxn-1;i>=0;i--)  
    2.     inv[i]=(inv[i+1]*(i+1))%mod;  

    还有一个定理可以用来求组合数:(卢卡斯定理)


    代码:

    #include<cstdio>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    using namespace std;
    typedef long long ll;
    const int maxn = 1e6+5;
    #define mod 1000000007
    #define debug puts("ok!");
    ll fac[maxn];
    
    void init(){
    	fac[0] = fac[1] = 1;
    	for(int i = 2;i<maxn;i++) fac[i] = fac[i-1]*i%mod;
    }
    
    ll quick_mod(ll a,ll b){
    	ll res = 1;
    	while(b)
    	{
    		if(b&1) res = (res*a)%mod;
    		b >>= 1;
    		a = (a*a)%mod;
    	}
    	return res;
    }
    
    ll C(ll n,ll m){
    	if(m>n){
    		return 0;
    	} else {
    		return (fac[n]*(quick_mod(fac[n-m]*fac[m]%mod,mod - 2))%mod);	
    	}
    }
    
    ll lucas(ll n, ll m){
    	if(m == 0){
    		return 1;
    	} else {
    		return C(n%mod,m%mod)*(lucas(n/mod,m/mod))%mod;
    	}
    }
    
    int main()
    {
    	ll n,m;
    	init();
    	while(~scanf("%lld %lld",&n,&m))
    	{
    		printf("%lld
    ",lucas(n,m-2));
    	}
    	return 0;
    }

    参考blog:

    https://blog.csdn.net/raalghul/article/details/51752369

    https://blog.csdn.net/wyg1997/article/details/52152282

    http://www.voidcn.com/blog/qq_28954601/article/p-6227778.html、

    https://menyf.gitbooks.io/acm-icpc-template/6_%E6%95%B0%E8%AE%BA/%E9%80%86%E5%85%83.html。

  • 相关阅读:
    poj 1328 Radar Installation (贪心)
    hdu 2037 今年暑假不AC (贪心)
    poj 2965 The Pilots Brothers' refrigerator (dfs)
    poj 1753 Flip Game (dfs)
    hdu 2838 Cow Sorting (树状数组)
    hdu 1058 Humble Numbers (DP)
    hdu 1069 Monkey and Banana (DP)
    hdu 1087 Super Jumping! Jumping! Jumping! (DP)
    必须知道的.NET FrameWork
    使用记事本+CSC编译程序
  • 原文地址:https://www.cnblogs.com/Nlifea/p/11746034.html
Copyright © 2011-2022 走看看