1. 程式人生 > 其它 >洛谷 P4240 - 毒瘤之神的考驗(數論+複雜度平衡)

洛谷 P4240 - 毒瘤之神的考驗(數論+複雜度平衡)

數論+根號平衡複雜度的小技巧

洛谷題面傳送門

先扯些別的。

2021 年 7 月的某一天,我和 ycx 對話:

  • tzc:你做過哪些名字裡帶“毒瘤”的題目,我做過一道名副其實的毒瘤題就叫毒瘤,是個虛樹+dp
  • ycx:還有毒瘤之神的考驗
  • tzc:???那是個啥?
  • ycx:一道數論水題

然後我便做到了這個題,然後卻發現它一點也不水……

跑題了跑題了

首先我們顯然不可能硬著頭皮算 \(\varphi(ij)\),肯定要想辦法將 \(\varphi(ij)\) 中的 \(i,j\) 獨立開來。通過這題的套路可知 \(\varphi(ij)=\dfrac{\varphi(i)\varphi(j)\gcd(i,j)}{\varphi(\gcd(i,j))}\)

,於是下面我們就可以開始推式子了:

\[\begin{aligned} ans&=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\varphi(ij)\\ &=\sum\limits_{i=1}^n\sum\limits_{j=1}^m\dfrac{\varphi(i)\varphi(j)\gcd(i,j)}{\varphi(\gcd(i,j))}\\ &=\sum\limits_{d=1}^{\min(n,m)}\dfrac{d}{\varphi(d)}\sum\limits_{d\mid i}\sum\limits_{d\mid j}\varphi(i)\varphi(j)[\gcd(i,j)=d]\\ &=\sum\limits_{d=1}^{\min(n,m)}\dfrac{d}{\varphi(d)}\sum\limits_{i=1}^{n/d}\sum\limits_{j=1}^{m/d}\varphi(id)\varphi(jd)[i\perp j]\\ &=\sum\limits_{d=1}^{\min(n,m)}\dfrac{d}{\varphi(d)}\sum\limits_{p=1}^{\min(n,m)/d}\mu(p)\sum\limits_{i=1}^{n/dp}\sum\limits_{j=1}^{m/dp}\varphi(idp)\varphi(jdp) \end{aligned} \]

如果我們記 \(f(n)=\sum\limits_{d\mid n}\dfrac{d}{\varphi(d)}·\mu(\dfrac{n}{d})\)

\(S(n,m)=\sum\limits_{i=1}^m\varphi(ni)\),那麼

\[\begin{aligned} ans&=\sum\limits_{T=1}^{\min(n,m)}f(T)S(T,\lfloor\dfrac{n}{T}\rfloor)S(T,\lfloor\dfrac{m}{T}\rfloor) \end{aligned} \]

\(f\) 顯然可以調和級數地求出,有用的 \(S(n,m)\) 的個數也是 \(n\ln n\) 級別的,因此暴力計算上式可實現 \(Tn+n\ln n\) 的複雜度,無法通過。直接整除分塊看起來不太容易的亞子,這就使我們陷入了一個比較尷尬的局面。但是一個非常直觀的 observation 是當 \(T\)

比較大時,可能的 \((\lfloor\dfrac{n}{T}\rfloor,\lfloor\dfrac{m}{T}\rfloor)\) 組成的二元組並不是特別多。因此考慮從這個角度入手。記 \(B=316\),對於 \(T\le B\),我們暴力計算即可,對於 \(T>B\),寫個程式算一下可以發現 \(\sum\limits_{i=317}^{10^5}\lfloor\dfrac{10^5}{i}\rfloor^2\) 只有大概 \(3\times 10^7\) 的樣子,因此這部分我們就預處理時,暴力列舉所有可能的 \((\lfloor\dfrac{n}{T}\rfloor,\lfloor\dfrac{m}{T}\rfloor)\),然後字首和算一下貢獻,這樣每次詢問整除分塊即可求出這部分 \(T\) 的貢獻。

時間複雜度 \(T\sqrt{n}+n\sqrt{n}+n\ln n\),可以通過。

const int MAXN=1e5;
const int B=316;
int inv[MAXN+5],pr[MAXN/7+5],prcnt=0,phi[MAXN+5],vis[MAXN+5],f[MAXN+5],mu[MAXN+5];
vector<int> s[MAXN+5],ss[B+4][B+4];
void init(){
	for(int i=(inv[0]=inv[1]=1)+1;i<=MAXN;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
	phi[1]=mu[1]=1;
	for(int i=2;i<=MAXN;i++){
		if(!vis[i]) phi[i]=i-1,mu[i]=-1,pr[++prcnt]=i;
		for(int j=1;j<=prcnt&&pr[j]*i<=MAXN;j++){
			vis[pr[j]*i]=1;
			if(i%pr[j]==0){phi[i*pr[j]]=phi[i]*pr[j];break;}
			phi[i*pr[j]]=phi[i]*phi[pr[j]];mu[i*pr[j]]=-mu[i];
		}
	}
	for(int i=1;i<=MAXN;i++) for(int j=i;j<=MAXN;j+=i)
		f[j]=(0ll+f[j]+1ll*i*inv[phi[i]]%MOD*mu[j/i]%MOD+MOD)%MOD;
	for(int i=1;i<=MAXN;i++){
		s[i].resize(MAXN/i+1);
		for(int j=1;j<=MAXN/i;j++) s[i][j]=(s[i][j-1]+phi[i*j])%MOD;
	}
	for(int i=1;i<=B+1;i++) for(int j=1;j<=B+1;j++){
		ss[i][j].pb(0);int sum=0;
		for(int k=B+1;k<=MAXN;k++){
			if(k*i>MAXN||k*j>MAXN) break;
			sum=(sum+1ll*f[k]*s[k][i]%MOD*s[k][j])%MOD;
			ss[i][j].pb(sum);
		}
	}
}
int main(){
	init();int qu;scanf("%d",&qu);
	while(qu--){
		int n,m;scanf("%d%d",&n,&m);int res=0;
		for(int i=1;i<=B;i++) res=(res+1ll*f[i]*s[i][n/i]%MOD*s[i][m/i])%MOD;
		for(int l=B+1,r;l<=min(n,m);l=r+1){
			r=INF;
			if(n/l) chkmin(r,n/(n/l));
			if(m/l) chkmin(r,m/(m/l));
			res=(0ll+res+ss[n/l][m/l][r-B]-ss[n/l][m/l][l-B-1]+MOD)%MOD;
		}
		printf("%d\n",res);
	}
	return 0;
}