hdu5608 function (莫比烏斯反演+杜教篩)
阿新 • • 發佈:2018-12-09
題意:根據式子
求
想出這題的做法後看了幾個部落格想驗證一下,然而看了四五篇部落格發現博主的做法都和我不一樣...用自己的想法AC後,發現時間比大部分程式碼快,直接到rank2,然後略微優化了一下線性篩預處理部分(理論上預處理到n的2/3次方也就是1e6最優,不過這題似乎2e6最優。)
這是我AC後找的某一個博主寫的題解,裡面的第一種做法和我的比較接近,第二種則是絕大部分人的做法:博文連結
可能還是有人和我一樣做的,但是懶得去一個個翻部落格了= =
下面開始說我的做法吧題目給出的式子很容易想到莫比烏斯反演,令,則有,則由莫比烏斯反演有。
因此所求式子可轉化為,再將這個式子轉化一下,可變為
(雖然寫過兩道杜教篩的題...但是並沒有自己算過它的複雜度,所以這裡的複雜度我只知道杜教篩O(),數論分塊O(),但是不會算。。。會算的盆友可以在評論裡教教我orz(一些直接說複雜度是O()的博文只說了分塊的複雜度啊= =))
程式碼如下:
(暗戳戳秀一波)
#include<bits/stdc++.h> using namespace std; #define For(i,a,b) for(int i=a;i<=b;i++) #define ll long long const int maxn=2000000; const int mod=1e9+7; int prime[150000],num,miu[maxn+5]; bool vst[maxn+5]; inline ll qpow(ll a,ll b){ ll res=1; while(b){ if(b&1) res=res*a%mod; b>>=1; a=a*1ll*a%mod; } return res; } inline void Pre(){ miu[1]=1; for (int i=2;i<=maxn;i++){ if (!vst[i]) prime[++num]=i,miu[i]=-1; for (int j=1;j<=num && (ll)i*prime[j]<=maxn;j++){ vst[i*prime[j]]=1; if (i%prime[j]==0){ miu[i*prime[j]]=0; break; } miu[i*prime[j]]=miu[i]*miu[prime[j]]; } } for (int i=1;i<=maxn;i++) miu[i]+=miu[i-1]; } unordered_map<ll,int> S; inline int Sum(ll n){ if (n<=maxn) return miu[n]; if (S.find(n)!=S.end()) return S[n]; int tem=1; ll l,r; for (l=2;l*l<=n;l++) tem-=Sum(n/l); for (ll tt=n/l;l<=n;l=r+1,tt--){ r=n/tt; tem-=(r-l+1)*Sum(tt); } return S[n]=tem; } int inv3; inline int solve(ll n){ ll l,r,tmp=0; for(l=1;l*l<=n;l++){ if(l<=2)continue; tmp=(tmp+(l*l-3*l+2)*Sum(n/l))%mod; } for(ll tt=n/l;l<=n;l=r+1,tt--){ r=n/tt; tmp=(tmp+((r*(r-1)%mod*(r-2)%mod*inv3)%mod-((l-1)*(l-2)%mod*(l-3)%mod*inv3)%mod)%mod*Sum(tt))%mod; } return (int)((tmp%mod+mod)%mod); } int t,n; int main(){ Pre(); inv3=qpow(3,mod-2); scanf("%d",&t); while(t--){ scanf("%d",&n); printf("%d\n",solve(n)); } return 0; }