BZOJ 3944 - Sum(杜教筛)
题目
子问题:
因为
所以
辣么
同理
子问题:
因为
所以
辣么
两个子问题都可以用杜教筛下求解
代码:
#include<cstdio>
#include<cmath>
#include<cstring>
#include<map>
#include<vector>
#include<iostream>
#include<algorithm>
#define maxn 5000000
using namespace std;
inline int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
map<long long,long long>Mu,Phi;
bool not_prime[maxn+5];
long long phi[maxn+5],mu[maxn+5],prime[maxn+5],cnt;
long long n;
inline void init()
{
mu[1]=phi[1]=1;
for(int i=2;i<=maxn;i++)
{
if(!not_prime[i])prime[++cnt]=i,mu[i]=-1,phi[i]=i-1;
for(int j=1;j<=cnt&&i*prime[j]<=maxn;j++)
{
not_prime[i*prime[j]]=1;
if(i%prime[j])mu[i*prime[j]]=-mu[i],phi[i*prime[j]]=phi[i]*phi[prime[j]];
else{phi[i*prime[j]]=phi[i]*prime[j];break;}
}
}
for(int i=1;i<=maxn;i++)phi[i]+=phi[i-1],mu[i]+=mu[i-1];
}
inline long long solvephi(long long x)
{
if(x<=maxn)return phi[x];
if(Phi.count(x))return Phi[x];
long long Sum=1ll*x*(1ll*x+1)/2;
for(long long i=2,j;i<=x;i=j+1)
j=min(x/(x/i),x),Sum-=1ll*(j-i+1)*solvephi(x/i);
return Phi[x]=Sum;
}
inline long long solvemu(long long x)
{
if(x<=maxn)return mu[x];
if(Mu.count(x))return Mu[x];
long long Sum=1;
for(long long i=2,j;i<=x;i=j+1)
j=min(x/(x/i),x),Sum-=1ll*(j-i+1)*solvemu(x/i);
return Mu[x]=Sum;
}
int main()
{
init();
int T=getint();
while(T--)
{
n=getint();
printf("%lld %lld\n",solvephi(n),solvemu(n));
}
}