• [hdu-6608] Fansblog 威尔逊定理 质数的密度分布 2019 多校 3


    题目链接:

    http://acm.hdu.edu.cn/showproblem.php?pid=6608

    题目大意:给一个质数P (1e9≤p≤1e14),找比它小的最大质数Q,求Q! Module P
     
    1.质数密度分布:质数分布的比较密,在p周围100内应该能遇到质数,所以q可以从大到小枚举
    2.判断10^14 内数x是否为质数,只用提前筛出1^7 内质数看是否有x因子
    3.威尔逊定理 (p1)!1(mod p) 当p是质数时 ,实际上就是 (p-1)! mod p =p-1 求Q! MOD P = (P-1)!/(P-1)/(P-2).../(Q+1) MOD P  =(P-1)*INF(P-1)*INF(P-2)*...*(Q+1)MOD P
    INF(x)为 x逆元
    4.求逆元方法:①费马小定理 a*ap-2 1 (mod p)  用快速幂求出ap-2即求出aa的逆元.
    ②扩展欧几里得 xa1 mod p  解 xa+yp=1
    因为p太大,用费马小定理会超时,用扩展欧几里得求
    ps:p太大,直接(p-1)*(p-2)时会超long long,用快速乘(把乘法分解为多次加法,一边加一边取模)
    #include<bits/stdc++.h>
    using namespace std;
    //( p -1 )! ≡ -1 =p-1( mod p )  当p为素数时
    long long  const n=1e7+100;
    typedef long long ll;
    ll prime[n];
    ll p,q,cntprime,ans;
    
    bool flag[n];
    void exgcd(ll a,ll b,ll &x,ll &y){//扩展欧几里得求逆元快一点,用费马小定理 a^(p-2)  由于p太大,容易超时
    	if(!b){x=1;y=0;}
    	else {exgcd(b,a%b,y,x);y-=x*(a/b);}
    }
    ll getinv(ll a){//求q!%p  相当于= (p-1)!/(p-1)/(p-2)...(q+1)%p  用逆元  
    	ll x,y;
    	exgcd(a,p,x,y);
    	while(x<0)x+=p;
    	return x;
    }
    ll mul(ll a,ll b){//快速乘  防止longlong 相乘会炸longlong
        ll ret=0;
        for(;b;b>>=1,a=(a<<1)%p)
          if(b&1) ret=(ret+a)%p;
        return ret%p;
    }
    void getans(){
    	ans=p-1;
    	for(ll i=p-1;i>q;i--){
    		ans=mul(getinv(i),ans);//用快速乘
    	}
    }
    void getprime(){//判断1e14内的质数,只需看有无1e7内的质数
    	cntprime=0;
    	for (ll i = 2; i <= 1e7; i++)
    	{
        if (!flag[i]) prime[++cntprime] = i;
        for (int j = 1; j <= cntprime && prime[j] * i <= n; j++)
        {
            flag[i * prime[j]] = true;
            if (i % prime[j] == 0)
                break;
        }
    	}
    }
    void getq(){//素数密度分布,素数分布的比较密集 			cout<<p-q<<endl;
    //可以从大到小枚举,判断是否为质数,出现的比较快 
    	bool ok;
    	for(ll i=p-2;i>=2;i--){
    		ok=false;
    		ll z=(long long)sqrt(i);
    		for(int j=1;j<=cntprime&&prime[j]<=z;j++)if(i%prime[j]==0){ok=true;break;}
    		if(!ok){q=i;return ;}	
    	}
    
    }
    
    int main(){
    	int t;
    	scanf("%d",&t);
    	getprime();
    	while(t--){
    		scanf("%lld",&p);
    		getq();
    		getans();
    		cout<<ans<<endl;
    	}
    
    	return 0;
    }
    
     
     
     
     
     
  • 相关阅读:
    JAVA 打开文件乱码
    单引号和双引号的区别
    global和$GLOBALS[]的区别
    php统计数组元素个数
    PHP5.3x不再支持ereg和eregi
    discuz论坛diy标签
    网页特效过渡功能
    discuz缓存机制
    php数组中删除元素
    speedphp是个不错的框架
  • 原文地址:https://www.cnblogs.com/conver/p/11273386.html
Copyright © 2020-2023  润新知