尤拉線性篩&尤拉函式&莫比烏斯函式
一:
莫比烏斯反演:
vijos1889
描述
小島: 什麼叫做因數分解呢?
doc : 就是將給定的正整數n, 分解為若干個素數連乘的形式.
小島: 那比如說 n=12 呢?
doc : 那麼就是 12 = 2 X 2 X 3 呀.
小島: 嗚嗚, 好難, 居然素數會重複出現, 如果分解後每一個素數都只出現一次, 我就會.
wish: 這樣來說, 小島可以正確分解的數字不多呀.
doc : 是呀是呀.
wish: 現在問題來了, 對於給定的k, 第 k 個小島無法正確分解的數字是多少?
格式
輸入格式
輸入只有一行, 只有一個整數 k.
輸出格式
輸出只有一行, 只有一個整數, 表示小島無法正確分解出來的第k個數字.
樣例1
樣例輸入1[複製]
10
樣例輸出1[複製]
27
分析:
二分答案,判斷ans前面偶遇多少個含有平方數的數
如何判斷呢?_?
sum代表x前面有sum個符合要求的數
sum=∑mu[i]*x/(i*i) (i=2~sqrt(x))
程式碼如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define int long long
using namespace std;
const int maxn=200005,N=160000;
int prime[maxn],mu[maxn],vis[maxn],cnt,k;
inline int read(){
char ch=getchar();
int f=1,x=0;
while(!(ch>='0'&&ch<='9')){
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9' )
x=x*10+ch-'0',ch=getchar();
return f*x;
}
int check(int x){
int sum=0,lala=sqrt(x);
for(int i=2;i<=lala;i++)
sum-=mu[i]*x/(i*i);
return sum;
}
signed main(void){
k=read();
memset(vis,0,sizeof(vis)),cnt=0;
for(int i=2;i<=N;i++){
if(!vis[i])
prime[++cnt]=i,mu[i]=-1;
for(int j=1;j<=cnt&&prime[j]*i<=N;j++){
vis[i*prime[j]]=1;
if(i%prime[j]==0){
mu[i*prime[j]]=0;
break;
}
else
mu[i*prime[j]]=-mu[i];
}
}
int l=k,r=25505460948LL,ans;
while(l<=r){
int mid=(l+r)>>1;
if(check(mid)>=k)
ans=mid,r=mid-1;
else
l=mid+1;
}
cout<<ans<<endl;
return 0;
}
二:
尤拉函式–>gcd求和
BZOJ 2705
首先這道題要用O(n^(1/2))的演算法來求尤拉函式
接下來我們先假設你會求,看如何求∑gcd
我們以∑(i,6) (i=1~6)為例
∑gcd(i,6)=
gcd(1,6)+gcd(5,6)—>1*2—>1*φ(6/1)
+gcd(2,6)+gcd(4,6)—>2*2—>2*φ(6/2)
+gcd(3,6)—>3*1—>3*φ(6/3)
+gcd(6,6)—>6*1—>6*φ(6/6)
哎??有沒有發現什麼
所以我們可以得到下面的式子
∑gcd(i,n)=∑(n%x==0?1:0)xφ(n/x)
接下來就要看怎麼求尤拉函數了
int phi(int x){
int t=x;//t用來計數
for(int i=2;i<=m;i++)//m=sqrt(n)
if(x%i==0){
t=t/i*(i-1);//1~x這些數可以分為長度為i的若干區間,每個區間中都有一個數與x不互質,所以只能取i-1個數
while(x%i==0)
x/=i;//在x中除去i
}
if(x>1)
t=t/x*(x-1);
return t;
}
程式碼如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define int long long
using namespace std;
int n,ans,m;
int phi(int x){
int t=x;
for(int i=2;i<=m;i++)
if(x%i==0){
t=t/i*(i-1);
while(x%i==0)
x/=i;
}
if(x>1)
t=t/x*(x-1);
return t;
}
signed main(void){
scanf("%lld",&n);
m=sqrt(n),ans=0;
for(int i=1;i<=m;i++)
if(n%i==0){
ans+=i*phi(n/i);
if(i*i<n)
ans+=n/i*phi(i);
}
cout<<ans<<endl;
return 0;
}
接下來是O(n)的演算法:
程式碼如下:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int maxn=160000+5;
int vis[maxn],prime[maxn],phi[maxn],cnt;
signed main(void){
memset(vis,0,sizeof(vis)),cnt=0;
for(int i=2;i<=10000;i++){
if(!vis[i])
prime[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt&&prime[j]*i<=10000;j++){
vis[prime[j]*i]=1;
if(i%prime[j]==0){
phi[i*prime[j]]=prime[j]*phi[i];
break;
}
else
phi[i*prime[j]]=(prime[j]-1)*phi[i];
}
}
for(int i=1;i<=100;i++)
cout<<phi[i]<<" ";
cout<<endl;
return 0;
}
by >o< neighthorn