[BZOJ3944]Sum
试题描述
输入
一共 \(T+1\) 行
第 \(1\) 行为数据组数 \(T(T \le 10)\)
第 \(2\) ~ \(T+1\) 行每行一个非负整数 \(N\),代表一组询问
输出
一共 \(T\) 行,每行两个用空格分隔的数 \(ans1,ans2\)
输入示例
6
1
2
8
13
30
2333
输出示例
1 1
2 0
22 -2
58 -3
278 -3
1655470 2
数据规模及约定
见“试题描述”和“输入”
题解
杜教筛模板(这个居然也有模板。。。)
既然用了 markdown 就再把核心公式打一遍吧。
令 \(F(n) = \sum_{i=1}^n {\mu (i)}\),\(G(n) = \sum_{i=1}^n {\varphi(i)}\)
\(\sum_{i=1}^n {F(\lfloor \frac{n}{i} \rfloor)} = \sum_{i=1}^n {\sum_{d|i}} {\mu (d)} = 1 \Rightarrow F(n) = 1 - \sum_{i=2}^n {F(\lfloor \frac{n}{i} \rfloor)}\)
\(\sum_{i=1}^n {G(\lfloor \frac{n}{i} \rfloor)} = \sum_{i=1}^n {\sum_{d|i}} {\varphi (d)} = \frac{n(n+1)}{2} \Rightarrow G(n) = \frac{n(n+1)}{2} - \sum_{i=2}^n {G(\lfloor \frac{n}{i} \rfloor)}\)
注意这题的数据范围,\(n\) 加个 \(1\) 就爆 int 了!!!建议把所有加号搜索一下然后依次开 long long。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <map>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
int read() {
int x = 0, f = 1; char c = getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
#define LL long long
#define maxn 2500010
int prime[maxn], cp, _phi[maxn], mu[maxn], su[maxn];
LL sp[maxn];
bool vis[maxn];
map <int, LL> phi;
map <int, int> u;
void init() {
sp[1] = su[1] = 1;
rep(i, 2, maxn - 1) {
if(!vis[i]) prime[++cp] = i, mu[i] = -1, _phi[i] = i - 1;
for(int j = 1; j <= cp && i * prime[j] < maxn; j++) {
vis[i*prime[j]] = 1;
if(i % prime[j] == 0) {
mu[i*prime[j]] = 0;
_phi[i*prime[j]] = _phi[i] * prime[j];
break;
}
mu[i*prime[j]] = -mu[i];
_phi[i*prime[j]] = _phi[i] * (prime[j] - 1);
}
su[i] = su[i-1] + mu[i];
sp[i] = sp[i-1] + _phi[i];
}
return ;
}
LL calcphi(int n) {
if(n < maxn) return sp[n];
if(phi.count(n)) return phi[n];
LL ans = ((LL)n + 1) * n >> 1;
for(int i = 2; i <= n;) {
int j = min(n / (n / i), n);
ans -= calcphi(n / i) * (j - i + 1);
if(j >= n) break;
i = j + 1;
}
return phi[n] = ans;
}
int calcu(int n) {
if(n < maxn) return su[n];
if(u.count(n)) return u[n];
int ans = 1;
for(int i = 2; i <= n;) {
int j = min(n / (n / i), n);
ans -= calcu(n / i) * (j - i + 1);
if(j >= n) break;
i = j + 1;
}
return u[n] = ans;
}
int main() {
init();
int T = read();
while(T--) {
int n = read();
printf("%lld %d\n", calcphi(n), calcu(n));
}
return 0;
}