【51nod-1239&1244】欧拉函数之和&莫比乌斯函数之和 杜教筛
题目链接:
1239:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1239
1244:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1244
杜教筛裸题,不过现在我也只会筛这俩前缀和...
$$s(n)=\sum _{i=1}^{n}f(i)$$
那么就有:
$$\sum_{i=1}^{n}f(i)\lfloor \frac{n}{i} \rfloor=\sum_{i=1}^{n}s(\lfloor \frac{n}{i} \rfloor)=s(n)+\sum_{i=2}^{n}s(\lfloor \frac{n}{i} \rfloor)$$
移项得到:
$$s(n)=\sum_{i=1}^{n}f(i)\lfloor \frac{n}{i} \rfloor-\sum_{i=2}^{n}s(\lfloor \frac{n}{i} \rfloor)$$
对于欧拉函数,$f(n)=\phi(n)$
$$\sum_{i=1}^{n}\phi(i)\lfloor \frac{n}{i} \rfloor=\sum_{i=1}^{n}\sum_{d|n}\phi(d)=\sum_{i=1}^{n}i=\frac{n*(n+1)}{2}$$
对于莫比乌斯函数,$f(n)=\mu(n)$
$$\sum_{i=1}^{n}\mu(i)\lfloor \frac{n}{i} \rfloor=\sum_{i=1}^{n}\sum_{d|n}\mu(d)=\sum_{i=1}^{n}[i=1]=1$$
然后这两个公式就可以在线筛预处理$n^{\frac{2}{3}}$只后记忆化达到$O(n^{\frac{2}{3}})$的效率.
值得注意的就是,记忆化要写hash,以及不要忘了取模,筛欧拉函数前缀和时牵扯取模和除2,可以先讨论奇偶除掉2再计算。
1239:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> using namespace std; #define LL long long #define N 5000000 #define P 233333 #define MAXN 250000 #define MO 1000000007 int cnt,prime[N+10],flag[N+10]; LL X,phi[N+10]; inline void Pre(LL n) { flag[1]=1; phi[1]=1; for (LL i=2; i<=n; i++) { if (!flag[i]) prime[++cnt]=i,phi[i]=i-1; for (int j=1; j<=cnt && i*prime[j]<=n; j++) { flag[i*prime[j]]=1; if (!(i%prime[j])) {phi[i*prime[j]]=phi[i]*prime[j]; break;} phi[i*prime[j]]=phi[i]*(prime[j]-1); } } for (LL i=1; i<=n; i++) phi[i]=(phi[i]+phi[i-1])%MO; } struct Hash{ int next; LL i,x; }mp[MAXN]; int head[MAXN],tot; inline void Add(LL i,LL x) {int pos=i%P; tot++; mp[tot].next=head[pos]; head[pos]=tot; mp[tot].i=i; mp[tot].x=x;} inline LL Sum(LL x) { if (x<=N) return phi[x]; else { int pos=x%P; for (int i=head[pos]; i; i=mp[i].next) if (mp[i].i==x) {return mp[i].x;} } LL sum=0,s=0; for (LL i=2,j; i<=x; i=j+1) j=x/(x/i),(sum+=Sum(x/i)%MO*(j-i+1)%MO)%=MO; if (x&1) s=(((x+1)/2)%MO)*(x%MO)%MO; else s=((x/2)%MO)*((x+1)%MO)%MO; sum=(s-sum+MO)%MO; Add(x,sum); return sum; } int main() { scanf("%lld",&X); Pre(N); printf("%lld\n",Sum(X)); return 0; }
1244
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> using namespace std; #define LL long long #define P 233333 #define N 5000000 #define MAXN 250000 int cnt,prime[N+10],flag[N+10]; LL L,R,mu[N+10]; inline void Pre(LL n) { flag[1]=1; mu[1]=1; for (LL i=2; i<=n; i++) { if (!flag[i]) prime[++cnt]=i,mu[i]=-1; for (int j=1; j<=cnt && i*prime[j]<=n; j++) { flag[i*prime[j]]=1; if (!(i%prime[j])) {mu[i*prime[j]]=0; break;} mu[i*prime[j]]=-mu[i]; } } for (LL i=1; i<=n; i++) mu[i]+=mu[i-1]; } struct Hash{ int next; LL i,x; }mp[MAXN]; int head[MAXN],tot; inline void Add(LL i,LL x) {int pos=i%P; tot++; mp[tot].next=head[pos]; head[pos]=tot; mp[tot].i=i; mp[tot].x=x;} inline LL Sum(LL x) { if (x<=N) return mu[x]; else { int pos=x%P; for (int i=head[pos]; i; i=mp[i].next) if (mp[i].i==x) {return mp[i].x;} } LL sum=0; for (LL i=2,j; i<=x; i=j+1) j=x/(x/i),sum+=Sum(x/i)*(j-i+1); Add(x,1LL-sum); return 1LL-sum; } int main() { scanf("%lld%lld",&L,&R); Pre(N); printf("%lld\n",Sum(R)-Sum(L-1)); return 0; }