省选模版复习——线性筛法

来源:互联网 发布:ubuntu16 安装mysql 编辑:程序博客网 时间:2024/05/16 01:56

bzoj 3944 sum

求phi和mu的前缀和,n<2^31

首先orz叉院lyp大神的讲义。

以下为lyp大神讲义的公式推导过程(我不生产公式,我只是大神的搬运工~~)

假设我们需要求f(x)的前缀和

令g(n)=sigma f(d) (满足d|n)

     F(n)=sigma f(i) (1<=i<=n)

我们可以得到sigma g(i) (1<=i<=n)  =sigma f(i)*[n/i]  (1<=i<=n,   [x]表示x向下取整)  =   sigma F(n/i)(1<=i<=n)

移项可得F[n]  =  sigma g(i)  (1<=i<=n) -  sigma F(n/i)  (2<=i<=n)

直接递归复杂度为O( n^(3/4) )  (不要问我为什么。。。)

据说我们可以先将n^(2/3)内的答案先删出来,再记忆划搜索一下就可以做到O(  n^(2/3)   )

//虽然搞了这么多,还算是复习了一下线性筛法。。。。

#include <map>#include <cstdio>#include <cstring>#include <algorithm>using namespace std;const int Maxn=1000005;typedef long long LL;LL mu[Maxn],phi[Maxn],ans1,ans2,n;int prime[Maxn],i,j,tot,Case;bool check[Maxn];map <int,LL> vx,vy;void init(){  mu[1]=1; phi[1]=1;  for (i=2;i<Maxn;i++){  if (!check[i]){    prime[++tot]=i;    mu[i]=-1;    phi[i]=i-1;  }  for (j=1;j<=tot;j++){    if (prime[j]*i>=Maxn) break;    check[prime[j]*i]=1;    if (i%prime[j]==0){    mu[i*prime[j]]=0;    phi[i*prime[j]]=phi[i]*prime[j];    break;    } else    {    mu[i*prime[j]]=-mu[i];    phi[i*prime[j]]=phi[i]*(prime[j]-1);    }  }  }  for (i=1;i<Maxn;i++){  mu[i]+=mu[i-1];  phi[i]+=phi[i-1];  }}void calc(LL n,LL &x,LL &y){  if (n<Maxn) {x = phi[n], y = mu[n];return;}  if (vx[n]!=0){  x = vx[n]; y=vy[n];  return;  }  x = n*(n+1)/2; y = 1;  LL i, j, t1, t2;  for (i=2;i<=n;i=j+1){  j = n/(n/i);  calc(n/i,t1,t2);  x = x-t1*(j-i+1);  y = y-t2*(j-i+1);  }  vx[n] = x; vy[n] = y;}int main(){  freopen("3944.in","r",stdin);  freopen("3944.out","w",stdout);  init();  scanf("%d",&Case);  while (Case--){  scanf("%lld",&n);  calc(n,ans1,ans2);  printf("%lld %lld\n",ans1,ans2);  }  return 0;}


0 0