杭電多校Day3 1006 Fansblog

原文鏈接:https://blog.csdn.net/cloudy_happy/article/details/99571628

原題鏈接:http://acm.hdu.edu.cn/showproblem.php?pid=6608

題目大意:給你一個質數p;讓你找到比p小的最大質數q,然後求出q的階乘模p的結果。

解題思路:首先我們需要知道威爾遜定理:
對於一個質數p,p-1的階乘模除以p等於p-1;
(p-1)! %p=(p-1);(很nb)。
我們要求的是q的階乘模除以p,所以我們可以得到以下式子:

q! * (q+1) * (q+2) * … *(p-3) * (p-2) * (p-1) % p = (p-1);

而我們可以把(q+1) * (q+2) * … *(p-3) * (p-2) * (p-1) % p除過去,就變成了:

q! = (p-1) * inv(p-1) * inv(p-2) * inv(p-3) * … * inv(q+2) * inv(q+1)。

inv表示逆元。解出來就是答案。

但還有一點要注意,因爲p的範圍在1e9到1e14,兩個(p-1)相乘會爆long long,所以需要用到快速乘,(現學現用)。

大致就這些,詳細看Code:

#include<iostream>

using namespace std;
typedef long long ll;
ll prime[5] = {2,5,3,233,331};
bool check(ll x){//檢測是否爲素數 
	if(x==1||x==0) return 0;
	if(x==2) return 1;
	for(ll i=3;i*i<x;i++){
		if(x%i==0) return 0;
	}
	return 1;
}

ll ksc(ll a,ll b,ll p){//傳說中的快速乘 
	return (a*b-(ll)((long double)a/p*b)*p+p)%p;
}

ll qpow(ll a,ll b,ll p){//這個就是快速冪的至尊版,需要搭配快速乘使用 
	ll res=1;
	while(b){
		if(b&1) res=ksc(res,a,p);
		b>>=1;
		a=ksc(a,a,p);
	}
	return res;
}

bool Miller_Rabin(ll p)//這個是超級無敵秀的 米勒羅賓素數檢測模板 
{                      //比上面那個素數檢測快了好多倍 
    if(p < 2) return 0;//很顯然我是cv過來的 
    if(p != 2 && p % 2 == 0) return 0;
    ll s = p - 1;
    while(! (s & 1)) s >>= 1;
    for(int i = 0; i < 3; ++i)
    {
        if(p == prime[i]) return 1;
        ll t = s, m = qpow(prime[i], s, p);
        while(t != p - 1 && m != 1 && m != p - 1)
        {
            m = ksc(m, m, p);
            t <<= 1;
        }
        if(m != p - 1 && !(t & 1)) return 0;
    }
    return 1;
}

ll inv(ll x,ll p){//求解一個數的逆元 
	return qpow(x,p-2,p);
}

int main(){
	int t;
	cin>>t;
	while(t--){
		ll p,ans;
		cin>>p;
		ans=p-1;
		for(ll x=p-2;x>=1;x-=2){
			if(Miller_Rabin(x)){//這裏也可以換成check(x)檢測 
				for(ll j=x+1;j<p;j++){
					ans=ksc(ans,inv(j,p),p);
				}
				cout<<ans<<endl;
				break;
			}
		}
	}
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章