反演就告一段落啦。。。
这题主要是化式子需要灵活。。。主要有交换Σ,减小枚举范围两个技巧。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<map> #define maxn 5000050 #define mod 1000000007 using namespace std; int n,miu[maxn],prime[maxn],tot=0,ans=0; bool vis[maxn]; map <int,int> mp; void get_table() { miu[1]=1; for (int i=2;i<=maxn-50;i++) { if (!vis[i]) { vis[i]=true; prime[++tot]=i;miu[i]=-1; } for (int j=1;j<=tot && i*prime[j]<=maxn-50;j++) { vis[i*prime[j]]=true; if (i%prime[j]) miu[i*prime[j]]=-miu[i]; else {miu[i*prime[j]]=0;break;} } } for (int i=2;i<=maxn-50;i++) miu[i]+=miu[i-1]; } int ask(int n) { if (n<=maxn-50) return miu[n]; if (mp.find(n)!=mp.end()) return mp[n]; int ret=1,l=2,r; while (l<=n) { r=n/(n/l); ret=(ret-(long long)ask(n/l)*(r-l+1)+mod)%mod; l=r+1; } mp[n]=ret;return ret; } int main() { scanf("%d",&n);get_table(); int l=1,r; while (l<=n) { r=n/(n/l); int top=n/l,l1=1,r1,ret=0; while (l1<=top) {r1=top/(top/l1);ret=(ret+(long long)top/l1*(r1-l1+1)%mod)%mod;l1=r1+1;} ret=(long long)ret*ret%mod; ans=(ans+(long long)(ask(r)-ask(l-1)+mod)%mod*ret%mod)%mod; l=r+1; } printf("%d ",ans); return 0; }