套路題c++
圖片來自:git
https://blog.csdn.net/V5ZSQ/article/details/52116285spa
杜教篩思想,根號遞歸下去。.net
先搞出前綴和g(n)=∑f(i)code
而後尋求遞歸。∑g(n/i)=常數blog
這一步要運用給出的f(i)的關係,幹掉f遞歸
具體:圖片
向枚舉約數轉化,不斷交換求和,交換統計貢獻的部分。經過數學意義變成枚舉約數get
而後相似杜教篩便可
f的前1000000項,調和級數枚舉約數減去貢獻
#include<bits/stdc++.h> #define reg register int #define il inline #define fi first #define se second #define mk(a,b) make_pair(a,b) #define numb (ch^'0') using namespace std; typedef long long ll; template<class T>il void rd(T &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } template<class T>il void ot(T x){x/10?ot(x/10):putchar(x%10+'0');} template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) printf("%lld ",a[i]);putchar('\n');} namespace Miracle{ const int mod=1e9+7; const int M=1000000+6; int t,n; int f[M]; int qm(int x,int y){ int ret=1;while(y){ if(y&1) ret=(ll)ret*x%mod;x=(ll)x*x%mod;y>>=1; }return ret; } int ad(int x,int y){ return x+y>=mod?x+y-mod:x+y; } void sieve(int n){ for(reg i=1;i<=n;++i) f[i]=(ll)(i-1)*(i-2)%mod; for(reg i=1;i<=n;++i){ for(reg j=i+i;j<=n;j+=i){ f[j]=ad(f[j],mod-f[i]); } } for(reg i=1;i<=n;++i) f[i]=ad(f[i],f[i-1]); } map<int,int>mp; int inv6; int sol(int n){ if(n<=M-5) return f[n]; if(mp.find(n)!=mp.end()) return mp[n]; ll ret=(ll)(n-1)*n%mod*(2*n-1)%mod*inv6%mod; ret=ad(ret,mod-(ll)n*(n-1)/2%mod); for(reg i=2,x=0;i<=n;i=x+1){ x=(n/(n/i)); ret=ad(ret,mod-(ll)(x-i+1)*sol(n/i)%mod); } return mp[n]=ret; } int main(){ sieve(M-5); inv6=qm(6,mod-2); rd(t); while(t--){ rd(n);printf("%d\n",sol(n)); } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/3/8 11:16:20 */