在做這道題之前,我們首先來嘗試簽到題。
簽到題
我們定義一個函數:\(qiandao(x)\) 為小於等於 x 的數中與 x 不互質的數的個數。要求 \(\sum\limits _{i=l}^r qiandao(i)\)
容易發現 \(qiandao(x)\) 只需求 \(\phi(x)\),不互質的個數就是另外一半。
那么問題轉化為了如何篩出區間 \(\phi\) 的值。考慮到值域最大只有 \(1e12\)。並且區間長度小於一百萬,所以可以嘗試篩根號以內素數求解。
我們知道歐拉函數計算公式為 \(\prod_{d \mid p}\frac{d-1}{d}\) 其中 d 是 p 的質因數。
於是我們可以通過篩去根號以內每個質數計算大整數區間的歐拉值,而剩下篩不掉的一定是大於根號的質數,這個直接根據上式乘上就行了。
復雜度與自然對數有關,和調和級數類似。
小清新數學題
求解 \(\sum\limits_{i=l}^r\mu(i)\)
現在我們掌握了區間篩積性函數的方法,但是,這道題的數據范圍到了 \(1e18\)。我們不可能曬出十億以內的質數。
那么怎么辦呢?
注意到本題求得是莫比烏斯函數,根據莫比烏斯函數的定義,函數值只與質因子的個數有關,我們不需要求出具體的質數是誰,只需要知道有幾個就行。
於是我們仍然可以用一百萬以內的質數去篩。
剩下的情況分三種:
- \(res=q\times p\)
- \(res=q\)
- \(res=q^2\)
首先對於第一種情況,莫比烏斯函數值並不會改變。
第二種情況,取反即可。
第三種情況,直接就是 0。
第三種情況好判斷,只需判斷是否滿足 \(\sqrt{i}^2==i\) 。
對於一二種情況,實質實在判斷 res 是否是素數,於是引出了我們的另一個問題。如何判斷一個數是否是素數?
Miller_Rabin 算法
百度百科:
Miller-Rabin算法是目前主流的基於概率的素數測試算法,在構建密碼安全體系中占有重要的地位。通過比較各種素數測試算法和對Miller-Rabin算法進行的仔細研究,證明在計算機中構建密碼安全體系時, Miller-Rabin算法是完成素數測試的最佳選擇。通過對Miller-Rabin 算 法底層運算的優化,可以取得較以往實現更好的性能。[1] 隨着信息技術的發展、網絡的普及和電子商務的開展, 信息安全逐步顯示出了其重要性。信息的泄密、偽造、篡改 等問題會給信息的合法擁有者帶來重大的損失。在計算機中構建密碼安全體系可以提供4種最基本的保護信息安全的服 務:保密性、數據完整性、鑒別、抗抵賴性,從而可以很大 程度上保護用戶的數據安全。在密碼安全體系中,公開密鑰 算法在密鑰交換、密鑰管理、身份認證等問題的處理上極其有效,因此在整個體系中占有重要的地位。目前的公開密鑰 算法大部分基於大整數分解、有限域上的離散對數問題和橢 圓曲線上的離散對數問題,這些數學難題的構建大部分都需 要生成一種超大的素數,尤其在經典的RSA算法中,生成的素數的質量對系統的安全性有很大的影響。目前大素數的生 成,尤其是隨機大素數的生成主要是使用素數測試算法,本 文主要針對目前主流的Miller-Rabin 算法進行全面系統的分析 和研究,並對其實現進行了優化
這個看個熱鬧就完了,其實這個東西我們只用來判斷一個數是否是素數。以下內容說到的 p 默認為質數。
Miller Rabin基於費馬小定理:
證明:
-
對於數 \(a,2a,3a,.....(p-1)a\) 沒有 \(p\) 的倍數。
-
對於這一列數在模 \(p\) 意義下兩兩不同余。
-
這串數在模 \(p\) 意義下的余數構成 \(p-1\) 的一個排列。
於是有:
化簡左邊:
由威爾遜定理:
消去 \((p-1)!\) 得證。
費馬小定理成立,那么如果費馬小定理的逆命題成立我們不就可以判斷質數了嗎?
可惜,很長時間里人們確實是這樣認為的,直到強偽素數被人們發現,例如341。
再來介紹 二次探測定理:
如果滿足\(x^2\equiv1\pmod{p}\) 那么 x 的取值只有 1 或者 p-1。
證明:
轉換上式:
由唯一分解定理得到\(p \mid x+1\) 或 \(p\mid x-1\)
所以 \(x\equiv1 or -1\pmod{p}\)。
如果 x 不滿足上式則 p 一定是合數。
所謂 Miller Rabin 就是上面的結合。說白了就是多取幾個數多試幾次,這樣出錯的概率極低。
簽到題code
#include<bits/stdc++.h>
#define int long long
#define mod 666623333
using namespace std;
int su[10000001],cnt,ans[1000011],len,lst=0,l,r,shu[1000011];
bool bo[10000001];
inline void pre()
{ for(int i=2;i<=1000000;++i)
{ if(!bo[i])su[++cnt]=i;
for(int j=1;j<=cnt and i*su[j]<=1000000;++j)
{ bo[su[j]*i]=1;
if(i%su[j]==0)break;
}
}
}
signed main()
{ pre();scanf("%lld%lld",&l,&r);len=r-l+1;
for(int i=1;i<=len;++i)ans[i]=shu[i]=i+l-1;
for(int i=1;i<=cnt;++i)
{ int base=l/su[i];
for(int j=base;;++j)
{ if(j*su[i]>=l and j*su[i]<=r)
{ ans[j*su[i]-l+1]=(ans[j*su[i]-l+1]/su[i]*(su[i]-1));
while(shu[j*su[i]-l+1]%su[i]==0)shu[j*su[i]-l+1]/=su[i];
}
else if(j*su[i]>r)break;
}
}
for(int i=1;i<=len;++i)
{ if(shu[i]!=1)ans[i]=ans[i]/shu[i]*(shu[i]-1);
lst=(lst+(l+i-1-ans[i]))%mod;
}
printf("%lld\n",lst);
}
小清新數學題code
#include<bits/stdc++.h>
#define int long long
#define N 1000500
#define mfb __int128
using namespace std;
int su[N],cnt,tot,u[N],shu[N];
bool bo[N];long long l,r,ans;
inline int qpow(mfb a,mfb b){int mod=b+1,base=1;a%=mod;while(b){if(b&1)base=base*a%mod;a=a*a%mod;b>>=1;}return base;}
inline bool miller(int x)
{return (qpow(2,x-1)==1 );}
signed main()
{ scanf("%lld%lld",&l,&r);u[1]=1;
int lim=1e6;
for(int i=2;i<=lim;++i)
{ if(!bo[i])su[++cnt]=i,u[i]=-1;
for(int j=1;j<=cnt and i*su[j]<=lim;++j)
{ bo[i*su[j]]=1;u[i*su[j]]=-u[i];
if(i%su[j]==0){u[i*su[j]]=0;break;}
}
}
for(int i=l;i<=r;++i)u[i-l+1]=1,shu[i-l+1]=i;
for(int i=1;i<=cnt;++i)
{ int be=l/su[i]+(l%su[i]!=0);
for(int j=be;j*su[i]<=r;++j)
{ int id=j*su[i]-l+1;
int cnt=0;
while(shu[id]%su[i]==0)shu[id]/=su[i],++cnt,u[id]*=-1;
if(cnt>=2)u[id]=0;
}
}
for(int i=l;i<=r;++i)if(shu[i-l+1]!=1)
{ if((long long )sqrt(shu[i-l+1])*sqrt(shu[i-l+1])==shu[i-l+1]){u[i-l+1]=0;continue;}
if(miller(shu[i-l+1]))u[i-l+1]*=-1;
}
for(int i=l;i<=r;++i)ans+=u[i-l+1];printf("%lld\n",ans);
}
由於數據比較水,我又比較懶,上面只用了 femart。
Miller Rabin
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=100000+5;
int a[3]={2,7,61};
ll qm(ll x,ll y,ll mod){
ll ret=1;
while(y){
if(y&1) (ret*=x)%=mod;
(x*=x)%=mod;
y>>=1;
}
return ret;
}
bool che(ll a,ll x){
int s=0;
ll t;
ll lp=x-1;
while(!(lp&1)){
s++;
lp>>=1;
}
t=qm(a,lp,x);
if(t==1||t==x-1) return true;
if(s==0) return false;
ll las=t;
for(int i=0;i<s;i++){
las=t;
(t*=t)%=x;
if(t==1&&(las!=1&&las!=x-1)) return 0;
}
if(t!=1) return 0;
return 1;
}
int n,m;
bool M_R(ll n){
if(n==2||n==7||n==61) return true;
if(n<2||n%2==0||n%7==0||n%61==0) return false;
for(int i=0;i<3;i++){
ll b=a[i];
if(b!=n){
if(!che(b,n)) return false;
}
}
return true;
}
int main(){
scanf("%d%d",&n,&m);
ll x;
for(int i=1;i<=m;i++){
scanf("%lld",&x);
if(x==2||x==3||x==5||x==7||x==11||x==13){
puts("Yes");continue;
}
if(x%2==0||x%3==0||x%5==0||x%7==0||x%11==0||x%13==0){
puts("No");continue;
}
if(M_R(x)) puts("Yes");
else puts("No");
}
return 0;
}
Miller Rabin的板子為轉載,上面過的是線性篩。