@(BZOJ)[杜教筛]
Description
Input
一共T+1行
第1行为数据组数T(T<=10)
第2~T+1行每行一个非负整数N,代表一组询问
Output
一共T行,每行两个用空格分隔的数ans1,ans2
Sample Input
6
1
2
8
13
30
2333
Sample Output
1 1
2 0
22 -2
58 -3
278 -3
1655470 2
Solution
入门模板题.
注意一点点常数优化的技巧: 调用函数时传入参数的速度是很慢的, 要尽量控制传入参数的数量和参数的类型大小.
#include <cstdio>
#include <cstring>
const long long BND = 4000000;
int n;
long long sum1[BND], sum2[BND], vis[600], _sum[600];
inline void pretreat()
{
static int vis[BND], prm[BND], phi[BND], mu[BND];
memset(phi, 0, sizeof(phi));
int cnt = 0;
mu[1] = phi[1] = 1;
for(int i = 2; i < BND; ++ i)
{
if(! phi[i])
prm[cnt ++] = i, mu[i] = -1, phi[i] = i - 1;
for(int j = 0; j < cnt && i * prm[j] < BND; ++ j)
{
int crt = i * prm[j];
if(! (i % prm[j]))
{
mu[crt] = 0;
phi[crt] = phi[i] * prm[j]; // phi[k * prm[j] ^ (k + 1)] / phi[k * prm[j] ^ k] = prm[j]
break;
}
mu[crt] = - mu[i];
phi[crt] = phi[prm[j]] * phi[i];
}
}
sum1[0] = sum2[0] = 0;
for(int i = 1; i < BND; ++ i)
sum1[i] = sum1[i - 1] + phi[i], sum2[i] = sum2[i - 1] + mu[i];
}
long long getPhiSum(int crt)
{
if(crt < BND)
return sum1[crt];
int tmp = n / crt;
if(vis[tmp])
return _sum[tmp];
_sum[tmp] = crt * ((long long)crt + 1) / 2;
for(int i = 2, lst; i <= crt; i = lst + 1) {
lst = crt / (crt / i), _sum[tmp] -= getPhiSum(crt / i) * (lst - i + 1);
if (lst==n) break;
}
vis[tmp] = 1;
return _sum[tmp];
}
long long getMuSum(int crt)
{
if(crt < BND)
return sum2[crt];
int tmp = n / crt;
if(vis[tmp])
return _sum[tmp];
_sum[tmp] = 1;
for(int i = 2, lst; i <= crt; i = lst + 1) {
lst = crt / (crt / i), _sum[tmp] -= getMuSum(crt / i) * (lst - i + 1);
if (lst==n) break;
}
vis[tmp] = 1;
return _sum[tmp];
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("BZOJ3944.in", "r", stdin);
freopen("BZOJ3944.out", "w", stdout);
#endif
pretreat();
int T;
for(scanf("%d", &T); T --;)
{
scanf("%d", &n);
memset(vis, 0, sizeof(vis));
long long ans1 = getPhiSum(n);
memset(vis, 0, sizeof(vis));
long long ans2 = getMuSum(n);
printf("%lld %lld
", ans1, ans2);
}
}