P2522 [HAOI2011]Problem b 題解

博客園同步

原題鏈接

簡要題意:

i=abj=cd[gcd(i,j)==k]\sum_{i=a}^b \sum_{j=c}^d [\gcd(i,j)==k]

TT 組詢問.

本題沒有部分分,直接考慮一個式子:

i=1aj=1b[gcd(i,j)==k]\sum_{i=1}^a \sum_{j=1}^b [\gcd(i,j)==k]

=i=1akj=1bk[gcd(i,j)==1] = \sum_{i=1}^{\lfloor \frac{a}{k} \rfloor} \sum_{j=1}^{\lfloor \frac{b}{k} \rfloor} [\gcd(i,j)==1]

=i=1akj=1bkdgcd(i,j)μd = \sum_{i=1}^{\lfloor \frac{a}{k} \rfloor} \sum_{j=1}^{\lfloor \frac{b}{k} \rfloor} \sum_{d | \gcd(i,j)} \mu_d

=d=1min(a,b)μdadkbdk = \sum_{d=1}^{\min(a,b)} \mu_d \lfloor \frac{a}{dk} \rfloor \lfloor \frac{b}{dk} \rfloor

上面每一步都是在用 莫比烏斯反演 的性質,不加解釋。

那麼,我們只需要用 O(n)O(n) 的時間預處理 μ\mu,就可以用 整除分塊O(min(a,b))O(\sqrt{\min(a,b)}) 的時間內求解這樣一個式子。

那麼,已知 kk,令:

fa,b=i=1aj=1b[gcd(i,j)==k]f_{a,b} = \sum_{i=1}^a \sum_{j=1}^b [\gcd(i,j)==k]

則:

i=abj=cd[gcd(i,j)==k]=fb,dfb,c1fa1,d+fa1,c1\sum_{i=a}^b \sum_{j=c}^d [\gcd(i,j)==k] = f_{b,d} - f_{b,c-1} - f_{a-1,d} + f_{a-1,c-1}

(可以稍微參考一下二維前綴和得出)

所以我們以 O(5×104)O(5 \times 10^4) 預處理,O(nmin(a,b))O(n \sqrt{\min(a,b)}) 詢問解決了本題。

時間複雜度:O(a+na)O(a + n\sqrt{a}).

實際得分:100pts100pts.(需要適度卡常)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=1e6+1;

inline ll read(){char ch=getchar(); ll f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	ll x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}

int prime[N],mu[N],sum[N];
int cnt=0,k; bool h[N];

inline void Euler(int n) {
	mu[1]=1; for(register int i=2;i<=n;i++) {
		if(!h[i]) mu[i]=-1,prime[++cnt]=i;
		for(int j=1;j<=cnt && i*prime[j]<=n;j++) {
			h[i*prime[j]]=1;
			if(i%prime[j]==0) break;
			mu[i*prime[j]]-=mu[i];
		}
	} for(int i=1;i<=n;i++) sum[i]=sum[i-1]+mu[i];
} //預處理 mu 的前綴和

inline int min(int a,int b) {return a<b?a:b;}
inline ll calc(int a,int b) {
	ll ans=0;
	for(register int i=1;i<=min(a,b);) {
		int t=min(a/(a/i),b/(b/i));
		ans+=1ll*(a/k/i)*(b/k/i)*(sum[t]-sum[i-1]);
		i=t+1;
	} return ans; //整除分塊
}

int main() {
	Euler(N-1); int T=read(); while(T--) {
		int a=read(),b=read(),c=read(),d=read(); k=read();
		printf("%lld\n",calc(b,d)-calc(b,c-1)-calc(a-1,d)+calc(a-1,c-1));
	}
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章