BZOJ 3944: Sum (杜教筛模板)

来源:互联网 发布:php中的魔术常量 编辑:程序博客网 时间:2024/05/17 21:55

题目传送门


题目分析

杜教筛模板题,人生中第一道杜教筛。

在这里推荐一篇非常棒的文章。【skywalkert’s space】
相信大多数人都是从这里开始了解和学习杜教筛的。

解题方法我就不一条公式一条公式的敲进去了,直接引用该文章中的片段:

这里写图片描述

这里写图片描述

其实杜教筛就分为两个主要部分,一个是在所有询问之前的线性筛,预处理出n23的表。另一个部分就是在dfs中进行分块计算,同时用哈希map记忆化搜索。

ps:用hash一般比用map要快,我采用手写hash的做法,代码会略长(我就没写过几次hash)。
还有,一开始我RE到死,原因竟然是有几个地方爆了int(谁叫我作死开int),要注意处理强转的细节,或者多用long long。


代码

#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <cmath>#include <algorithm>#define N 1000010#define M 2333333using namespace std;typedef long long LL;int T, n, cnt;bool vis[N];int miu[N], prime[N];LL phi[N];int hash_n1[M], hash_n2[M], hash_v2[M];LL hash_v1[M];void Da(){    miu[1] = 1;    vis[1] = true;    phi[1] = 1ll;    for(int i = 2; i < N; i++){      if(!vis[i]){        prime[++cnt] = i;        miu[i] = -1;        phi[i] = (LL)(i-1);      }      for(int j = 1; j <= cnt && i * prime[j] < N; j++){        vis[i * prime[j]] = true;        if(i % prime[j] == 0){          miu[i * prime[j]] = 0;          phi[i * prime[j]] = phi[i] * (LL)prime[j];          break;        }        else{          miu[i * prime[j]] = -miu[i];          phi[i * prime[j]] = phi[i] * (LL)(prime[j]-1);        }      }    }    for(int i = 1; i < N; i++)  phi[i] += phi[i-1], miu[i] += miu[i-1];}LL Find1(int x){    int p = x % M;    while(hash_n1[p] && hash_n1[p] != x)  p = (p+1) % M;    if(!hash_n1[p])  return -1;    return hash_v1[p];}int Find2(int x){    int p = x % M;    while(hash_n2[p] && hash_n2[p] != x)  p = (p+1) % M;    if(!hash_n2[p])  return -1;    return hash_v2[p];}void Push1(LL v, int x){    int p = x % M;    while(hash_n1[p])  p = (p+1) % M;    hash_v1[p] = v;    hash_n1[p] = x;}void Push2(int v, int x){    int p = x % M;    while(hash_n2[p])  p = (p+1) % M;    hash_v2[p] = v;    hash_n2[p] = x;}LL Sum_Phi(int x){    if(x < N)  return phi[x];    LL res = Find1(x);    if(~ res)  return res;    else  res = (LL)x * ((LL)x+1) >> 1;    int last;    for(int i = 2;; i = last+1){      last = x/(x/i);      res -= (LL)(last-i+1) * Sum_Phi(x/i);      if(last >= x)  break;    }    Push1(res, x);    return res;}int Sum_Miu(int x){    if(x < N)  return miu[x];    int res = Find2(x);    if(~ res)  return res;    else  res = 1;    int last;    for(int i = 2;; i = last+1){      last = x/(x/i);      res -= (last-i+1) * Sum_Miu(x/i);      if(last >= x)  break;    }    Push2(res, x);    return res;}int main(){    freopen("bzoj3944.in", "r", stdin);    freopen("bzoj3944.out", "w", stdout);    Da();    scanf("%d", &T);    while(T --){      scanf("%d", &n);      printf("%lld %d\n", Sum_Phi(n), Sum_Miu(n));    }    return 0;}

原创粉丝点击