[bzoj3944] sum [杜教筛模板]
题面:
就是让你求$ \varphi\left(i\right) $以及$ \mu\left(i\right) $的前缀和
思路:
就是杜教筛的模板
我们把套路公式拿出来:
$ g\left(1\right)S\left(n\right)=\sum_{i=1}^{n}\left(g\ast f\right)\left(i\right)-\sum_{i=2}^{n}g\left(i\right)S\left(\frac ni\right) $
其中函数$f$分别为$\varphi$以及$\mu$
对于这两个函数有两个非常好用的卷积公式:
$\left(\mu\ast I\right)=\varepsilon$
$\left(\varphi\ast I\right)=id$
那么我们设g(x)=1,然后把g(x)带进去,两个前缀和就变成了这样的:
$S\left(n\right)=1-\sum_{i=2}^{n}S\left(\frac ni\right)$这个是$\mu$
$S\left(n\right)=\frac{n\ast\left(n+1\right)}{2}-\sum_{i=2}^{n}S\left(\frac ni\right)$这个是$\varphi$
然后递归,记忆化求和就可以了
注意最好写成一个递归处理两个答案......不然会T成狗
Code:
这里提供两个函数分开的版本,方便查看
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<map> 6 #define ll long long 7 using namespace std; 8 inline ll read(){ 9 ll re=0,flag=1;char ch=getchar(); 10 while(ch>'9'||ch<'0'){ 11 if(ch=='-') flag=-1; 12 ch=getchar(); 13 } 14 while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar(); 15 return re*flag; 16 } 17 ll phi[2000010],pri[2000010],tot=0,mu[2000010],n;bool vis[2000010]; 18 void init(){ 19 ll i,j,k;phi[1]=mu[1]=1;phi[0]=0; 20 for(i=2;i<=2000000;i++){ 21 if(!vis[i]){ 22 pri[++tot]=i;phi[i]=i-1;mu[i]=-1; 23 } 24 for(j=1;j<=tot;j++){ 25 k=i*pri[j];if(k>2000000) break; 26 vis[k]=1; 27 if(i%pri[j]==0){ 28 phi[k]=phi[i]*pri[j]; 29 mu[k]=0; 30 break; 31 } 32 phi[k]=phi[i]*phi[pri[j]]; 33 mu[k]=-mu[i]; 34 } 35 } 36 for(i=2;i<=2000000;i++) phi[i]=phi[i-1]+phi[i],mu[i]=mu[i-1]+mu[i]; 37 } 38 ll sum1(ll x){return x*(x+1)/2;} 39 ll v1[1000010],v2[1000010],m1[1000010],m2[1000010]; 40 ll S1(ll x){ 41 if(x<=2000000) return phi[x]; 42 ll re=sum1(x);ll i,j,t=n/x; 43 if(v1[t]) return m1[t]; 44 for(i=2;i<=x;i=j+1){ 45 j=x/(x/i); 46 re-=(j-i+1)*S1(x/i); 47 } 48 v1[t]=1; 49 return m1[t]=re; 50 } 51 ll S2(ll x){ 52 if(x<=2000000) return mu[x]; 53 ll re=1,i,j,t=n/x; 54 if(v2[t]) return m2[t]; 55 for(i=2;i<=x;i=j+1){ 56 j=x/(x/i); 57 re-=(j-i+1)*S2(x/i); 58 } 59 v2[t]=1; 60 return m2[t]=re; 61 } 62 int main(){ 63 ll T=read();init(); 64 while(T--){ 65 n=read();memset(v1,0,sizeof(v1));memset(v2,0,sizeof(v2)); 66 printf("%lld %lld\n",S1(n),S2(n)); 67 } 68 }