[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} \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;
}
posted @ 2017-12-12 21:23  xjr01  阅读(205)  评论(0编辑  收藏  举报