洛谷3708 koishi的数学题 (递推)

推了一会,一开始想用“余数求和”这题的方法,复杂度有点高,除法分块跑不过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);
}
posted @ 2018-09-02 19:15  BLUE_EYE  阅读(210)  评论(0编辑  收藏  举报