数论题。。。
来源:luogu秋令营的模拟赛1T2
这道题数据极大,\(N,T \leq 10000000\)。
求的是\(\sum_{i=1}^{n}{n \mod i}\)。
看上去就是余数求和。打那道题的除法分块做法有70分。
接下来介绍满分做法:
变成这个式子:
\[\sum_{i=1}^n{\lfloor \frac{n}{i} \rfloor \times i}=\sum_{i=1}^n{d_1(i)}\]
\(d_1(x)\)表示\(x\)的约数和。
这个东西可以用线性筛顺便搞出来。
这里粘两个博客,讲得很好,我就不讲了。。。
https://blog.csdn.net/controlbear/article/details/77527115
https://blog.csdn.net/wu_tongtong/article/details/79684277
所以可以线性求出这个东西,弄下前缀和。
然后用\(n^2\)减掉这个东西就是答案了。
代码:
#include<cstdio>
#define ll long long
const int maxn = 10000005;
const int N = maxn - 5;
bool notprime[maxn];
int prime[maxn], tot;
ll sd[maxn], sp[maxn];
ll pre[maxn];
ll read()
{
ll ans = 0, s = 1;
char ch = getchar();
while(ch > '9' || ch < '0'){ if(ch == '-') s = -1; ch = getchar(); }
while(ch >= '0' && ch <= '9') ans = (ans << 3) + (ans << 1) + ch - '0', ch = getchar();
return s * ans;
}
void init()
{
notprime[1] = true; sd[1] = 1; sp[1] = 1;
for(int i = 2; i <= N; i++)
{
if(!notprime[i])
{
prime[++tot] = i;
sd[i] = i + 1;
sp[i] = i + 1;
}
for(int j = 1; j <= tot && i * prime[j] <= N; j++)
{
notprime[i * prime[j]] = true;
if(i % prime[j] == 0)
{
sd[i * prime[j]] = sd[i] / sp[i] * (sp[i] * prime[j] + 1);
sp[i * prime[j]] = sp[i] * prime[j] + 1;
break;
}
else
{
sd[i * prime[j]] = sd[i] * sd[prime[j]];
sp[i * prime[j]] = 1 + prime[j];
}
}
}
for(int i = 1; i <= N; i++) pre[i] = pre[i - 1] + sd[i];
}
int main()
{
init();
ll T = read();
while(T--)
{
ll n = read();
printf("%lld\n", n * n - pre[n]);
}
return 0;
}