BZOJ 3944: Sum (杜教筛模板)

xiaoxiao2021-02-28  93

题目传送门


题目分析

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

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

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

其实杜教筛就分为两个主要部分,一个是在所有询问之前的线性筛,预处理出 n23 n 2 3 的表。另一个部分就是在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 2333333 using 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; }
转载请注明原文地址: https://www.6miu.com/read-60960.html

最新回复(0)