BZOJ 3944: Sum (杜教筛)

惊险…卡着时限过了…

注意有的地方可能爆int(因为有23112^{31}-1)

有几个地方可能会蜜汁RE,具体看代码注释

CODE

9000ms卡过2333

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 1000005;
int prime[MAXN/10], cnt;
LL mu[MAXN], phi[MAXN];
bool vis[MAXN];
inline void Pre_Work(int n) {
	mu[1] = phi[1] = 1;
	for(int i = 2; i <= n; ++i) {
		if(!vis[i])
			prime[++cnt] = i, mu[i] = -1, phi[i] = i-1;
		for(int j = 1; j <= cnt && i*prime[j] <= n; ++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);
		}
	}
	for(int i = 2; i <= n; ++i)
		mu[i] += mu[i-1], phi[i] += phi[i-1];
}
map<int, int>MU;
int sum_mu(int n) {
	if(n < MAXN) return mu[n];
	if(MU.count(n)) return MU[n];
	int re = 1;
	for(int i = 2, j; i > 0 && i <= n; i = j+1) { //这里i=j+1会爆int
		j = n/(n/i);
		re -= (j-i+1) * sum_mu(n/i);
	}
	return MU[n]=re;
}
map<int, LL>PHI;
LL sum_phi(int n) {
	if(n < MAXN) return phi[n];
	if(PHI.count(n)) return PHI[n];
	LL re = 1ll * n * (1ll*n+1) / 2; //这里+1也会爆int
	for(int i = 2, j; i > 0 && i <= n; i = j+1) { //这里i=j+1会爆int
		j = n/(n/i);
		re -= sum_phi(n/i) * (j-i+1);
	}
	return PHI[n]=re;
}
int main() {
	int n, T;
	scanf("%d", &T);
	Pre_Work(MAXN-1);
	while(T--) {
		scanf("%d", &n);
		printf("%lld %d
", sum_phi(n), sum_mu(n));
	}
}
原文地址:https://www.cnblogs.com/Orz-IE/p/12039292.html