[BZOJ3944]Sum

[BZOJ3944]Sum

试题描述

QAQ

输入

一共 (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} floor)} = sum_{i=1}^n {sum_{d|i}} {mu (d)} = 1 Rightarrow F(n) = 1 - sum_{i=2}^n {F(lfloor frac{n}{i} floor)})

(sum_{i=1}^n {G(lfloor frac{n}{i} floor)} = 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} floor)})

注意这题的数据范围,(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
", calcphi(n), calcu(n));
	}
	
	return 0;
}
原文地址:https://www.cnblogs.com/xiao-ju-ruo-xjr/p/8029780.html