以前一直都是贴板子。今天花了一点时间看了下证实,原来是如此简洁优雅。欧拉大神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"); } }