以前一直都是貼板子。今天花了一點時間看了下證實,原來是如此簡潔優雅。歐拉大神orzhtml
一。正確性。 對於任何一個大於合數C,設p爲C的最小質因子,令A=C/p,那麼顯然有A的最小質因子大於等於p,因而在進行到A枚舉已存儲的質數時,枚舉到A的最小質因子以前p必定被枚舉到了,因而C必定被篩出了。這就證實了全部的合數都會被篩出,也就證實了歐拉篩的正確性。c++
二。時間的線性。 只須要證實任何一個合數都不會被重複篩。 觀察歐拉篩的過程發現全部的合數都是以一個質數乘另外一個數的形式被篩出的。那麼設C=A·p1,p1是C的最小質因子,假設存在另外一組數知足C=B·p2,則有p2大於等於p1,而B的質因子必然包含p1,那麼當進行到B的時候,在枚舉到p1的時候就已經退出循環,因而C不會被重複篩出。即證實了時間複雜度。函數
這題要求: \(\sum_{i=a}^{b}\sum_{j=c}^{d}[gcd(i,j)=k]\)spa
能夠容斥一下變成四個形爲\(\sum_{i=1}^{x}\sum_{j=1}^{y}[gcd(i,j)=k]\)的和差。3d
對於這個式子:code
1)oi-wiki上的解法(硬算,對單位函數\(\epsilon(n)\)卷積展開):htm
一個重要結論:\(\sum_{d|n}\mu(d)=\epsilon(n)\)blog
\(f(x,y)=\sum_{i=1}^{x}\sum_{j=1}^{y}[gcd(i,j)=k]\)ci
\(=\sum_{i=1}^{\frac{x}{k}}\sum_{j=1}^{\frac{y}{k}}[gcd(i,j)=1]\)
\(=\sum_{i=1}^{\frac{x}{k}}\sum_{j=1}^{\frac{y}{k}}\epsilon(gcd(i,j))\)
\(=\sum_{i=1}^{\frac{x}{k}}\sum_{j=1}^{\frac{y}{k}}\sum_{d|gcd(i,j)}\mu(d)\)
\(=\sum_{i=1}^{\frac{x}{k}}\sum_{j=1}^{\frac{y}{k}}\sum_{d|gcd(i,j)}\mu(d)\)
\(=\sum_{d=1}^{\frac{x}{k}}\mu(d)\sum_{i=1}^{\frac{x}{k}}[d|i]\sum_{j=1}^{\frac{y}{k}}[d|j]\)
\(=\sum_{d=1}^{\frac{x}{k}}\mu(d)\frac{x}{kd}\frac{y}{kd}\)
預處理\(\mu(n)\)及其前綴和後可經過數論分塊在\(o(\sqrt{n})\)時間求出
2)某大牛莫比烏斯反演解法(構造一個\(F(n)\)十分直觀。
參見博客:大佬博客
AC代碼:
#include <bits/stdc++.h> using namespace std; const int maxn=50005; int Mo[maxn],prime[maxn],P[maxn]; int sumMo[maxn]; void getMo(){ int cnt=0; for(int i=1;i<maxn;i++) prime[i]=1; Mo[1]=1; prime[1]=0; for(int i=2;i<maxn;i++){ if(prime[i]){ P[++cnt]=i; Mo[i]=-1; } for(int j=1;j<=cnt&&i*P[j]<maxn;j++){ prime[i*P[j]]=0; if(i%P[j]==0){ Mo[i*P[j]]=0; break; } Mo[i*P[j]]=-Mo[i]; } } for(int i=1;i<maxn;i++) sumMo[i]=sumMo[i-1]+Mo[i]; } long long getans(int x,int y,int k){ if(x>y) swap(x,y); int n1=x/k,n2=y/k; long long ans=0; int i=1,j=1; while(i<=n1&&j<=n2){ int ni=n1/(n1/i),nj=n2/(n2/j); int tmp=min(ni,nj); ans += 1ll*(n1/i)*(n2/j)*(sumMo[tmp]-sumMo[i-1]); i=j=tmp+1; } return ans; } int main(){ int t; getMo(); cin >> t; while(t--){ int a,b,c,d,k; cin >> a >> b >> c >> d >> k; cout << getans(b,d,k)-getans(a-1,d,k)-getans(b,c-1,k)+getans(a-1,c-1,k) << endl; } return 0; }
題目要求 \(\sum_{i=1}^{n}gcd(\sqrt[3]{i},i)\)
首先數論分塊很容易有原式=\(\sum_{x=(\sqrt[3]{n})^3}^{n}gcd(\sqrt[3]{n},x)+\sum_{x=1}^{\sqrt[3]{n}}\sum_{i=x^3}^{(x+1)^3-1}gcd(x,i)\)
令\(\sqrt[3]{n}=r\)
此處有一個重要公式:\(\sum_{i=1}^{n}gcd(s,i)=\sum_{d|s}\frac{n}{d}\phi(d)\), \(\phi\)爲歐拉函數
因而\(\sum_{x=1}^{r}\sum_{i=x^3}^{(x+1)^3-1}gcd(x,i)\)
=\(\sum_{x=1}^{r}\sum_{d|x}(\frac{(x+1)^3-1}{d}-\frac{x^3-1}{d})\phi(d)\)
=\(\sum_{d=1}^{r}\phi(d)\sum_{i=1}^{\frac{r}{d}}(\frac{(di+1)^3-1}{d}-\frac{di^3-1}{d})\)
=\(\sum_{d=1}^{r}\phi(d)\sum_{i=1}^{\frac{r}{d}}(3di^2+3i+1)\)
能夠在\(O(\sqrt[3]{n})\)的時間求出答案。
AC代碼:
#include <bits/stdc++.h> using namespace std; typedef __int128 ll; typedef long long lll; inline __int128 read(){ __int128 x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x*f; } inline void print(__int128 x){ if(x<0){ putchar('-'); x=-x; } if(x>9) print(x/10); putchar(x%10+'0'); } const int N=1e7+5; int phi[N], p[N], cnt, vis[N]; void getphi() { phi[1] = 1; for (int i=2; i<N; ++i) { if (!vis[i]) p[++cnt]=i,phi[i]=i-1; for (int j=1,t; j<=cnt&&i*p[j]<N; ++j) { vis[t=i*p[j]] = 1; if (i%p[j]==0) {phi[t]=phi[i]*p[j];break;} phi[t]=phi[i]*phi[p[j]]; } } } int main(){ int T; scanf("%d",&T); getphi(); while(T--){ ll n; n=read(); int mod=998244353; lll ans=0; lll r=0; while(ll(r)*r*r-1<=n) r++; r-=2; for(int d=1;d<=r;d++){ lll tmp=r/d; lll pp; pp=tmp; pp=(pp+3*(tmp*(tmp+1)/2)%mod)%mod; pp=(pp+d*((tmp*(tmp+1)/2%mod)*(2*tmp+1)%mod)%mod)%mod; pp=pp*phi[d]%mod; ans=(ans+pp)%mod; } r++; ll tt=ll(r)*r*r-1; int rr=sqrt(r+0.5); for(int d=1;d<=rr;d++){ if(r%d==0){ ans=(ans+phi[d]*(n/d-tt/d))%mod; int tm=r/d; if(tm!=d){ ans=(ans+phi[tm]*(n/tm-tt/tm))%mod; } } } printf("%lld",ans); printf("\n"); } }