1. 程式人生 > >hdu5608 function (莫比烏斯反演+杜教篩)

hdu5608 function (莫比烏斯反演+杜教篩)

題目連結

題意:根據式子

想出這題的做法後看了幾個部落格想驗證一下,然而看了四五篇部落格發現博主的做法都和我不一樣...用自己的想法AC後,發現時間比大部分程式碼快,直接到rank2,然後略微優化了一下線性篩預處理部分(理論上預處理到n的2/3次方也就是1e6最優,不過這題似乎2e6最優。)
這是我AC後找的某一個博主寫的題解,裡面的第一種做法和我的比較接近,第二種則是絕大部分人的做法:博文連結
可能還是有人和我一樣做的,但是懶得去一個個翻部落格了= =

下面開始說我的做法吧題目給出的式子很容易想到莫比烏斯反演,令g(n)=n^{2}-3*n+2,則有g(n)=\sum_{d|n}f(d),則由莫比烏斯反演有f(n)=\sum_{d|n}g(d)*\mu(\frac{n}{d})

因此所求式子可轉化為\sum_{i=1}^{n}\sum_{d|i}g(d)*\mu(\frac{i}{d}),再將這個式子轉化一下,可變為\sum_{d=1}^{n}g(d)\sum_{i=1}^{\left \lfloor \frac{n}{d} \right \rfloor}\mu(i)

,這樣後面部分其實就是莫比烏斯函式的字首和,可以通過杜教篩求得,然後由數論分塊可知,出現的莫比烏斯函式字首和只有O(sqrt(n))個值,而前面部分就只需要每次求一塊的和,和後面部分乘起來再累加即可。

(雖然寫過兩道杜教篩的題...但是並沒有自己算過它的複雜度,所以這裡的複雜度我只知道杜教篩O(n^{\frac{2}{3}}),數論分塊O(\sqrt n),但是不會算。。。會算的盆友可以在評論裡教教我orz(一些直接說複雜度是O(\sqrt n)的博文只說了分塊的複雜度啊= =))

程式碼如下:

(暗戳戳秀一波)

#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;
}