推了一会,一开始想用“余数求和”这题的方法,复杂度有点高,除法分块跑不过1e6,但是其实这题有比较巧妙的写法。我们考虑f(n)和f(n+1)的递推关系,在不考虑取模的情况下,f(n+1)=f(n)+n+n+1这个是显然的(相当于每个数加一,再加上n+1),但是我们有恰好取模后为0的情况,我们记d(x)为一个数的约数和,那么f[i]=f[i-1]+2*i-1-d[i];即我们要减去取模后变成0的数,那么这题就等价于线性求d(n)啦,考虑到d(n)是个类似积性函数的东西,我们可以线性筛晒出来。
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
const ll N=1e6+5;
ll d[N],f[N],b[N],a[N],l,sum[N];
int main(){
ll n;scanf("%lld",&n);
for(ll i=2;i<=n;i++){
if(!b[i]) b[i]=1,a[++l]=i,sum[i]=1+i,d[i]=sum[i];
for(ll j=1;j<=l&&a[j]*i<=n;j++){
b[i*a[j]]=1;
if(i%a[j]==0){
sum[i*a[j]]=sum[i]*a[j]+1;
d[i*a[j]]=d[i]/sum[i]*sum[i*a[j]];break;
}d[i*a[j]]=d[i]*sum[a[j]];
sum[i*a[j]]=1+a[j];
}
}
// for(ll i=1;i<=n;i++){
// for(ll j=1;j*j<=i;j++)
// if(i%j==0) d[i]+=j+i/j;
// ll t=sqrt(i);if(t*t==i) d[i]-=t;
// }
for(ll i=1;i<=n;i++) f[i]=f[i-1]+2*i-1-d[i];
for(ll i=1;i<=n;i++) printf("%lld ",f[i]+(n-i)*i-1);
}