1. 程式人生 > 實用技巧 >[HAOI2011] Problem b

[HAOI2011] Problem b

前言

板題一號

題目

洛谷

題意:

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

講解

首先容斥

\(S(n,m)=\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k]\)

則原式可化為 \(S(b,d)-S(a-1,d)-S(b,c-1)+S(a-1,c-1)\)

\(f(k)=\sum_{i=1}^n\sum_{j=1}^m[\gcd(i,j)=k]\),即 \(S(n,m)\)

\(F(k)=\sum_{d|k}f(k)=\lfloor\dfrac{n}{k}\rfloor\lfloor\dfrac{m}{k}\rfloor\)

\(f(k)=\sum_{k|d}\mu(\dfrac{d}{k})F(d)=\sum_{k|d}\mu(\dfrac{d}{k})\lfloor\dfrac{n}{d}\rfloor\lfloor\dfrac{m}{d}\rfloor\)

我們將 \(\dfrac{d}{k}\) 換元,得到:

\(f(k)=\sum_{i=1}^{\frac{\min(n,m)}{k}}\mu(i)\lfloor\dfrac{n}{ik}\rfloor\lfloor\dfrac{m}{ik}\rfloor\)

然後數論分塊即可

程式碼

int mu[MAXN],prime[MAXN],pn,s[MAXN];
bool vis[MAXN];
void sieve(int x)
{
	s[1] = mu[1] = 1;
	for(int i = 2;i <= x;++ i)
	{
		if(!vis[i]) prime[++pn] = i,mu[i] = -1;
		for(int j = 1;j <= pn && i * prime[j] <= x;++ j)
		{
			vis[i * prime[j]] = 1;
			if(i % prime[j] == 0) break;
			mu[i * prime[j]] = -mu[i];
		}
		s[i] = s[i-1] + mu[i]; 
	}
}
int solve(int x,int y)
{
	if(x>y) swap(x,y);
	int ret = 0;
	for(int l = 1,r;l <= x;l = r+1)
	{
		r = Min(x/(x/l),y/(y/l));
		ret += (x / (l*k)) * (y / (l*k)) * (s[r] - s[l-1]);
	}
	return ret;
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	sieve(50000); 
	for(int T = Read(); T ;-- T)
	{
		a = Read(); b = Read(); c = Read(); d = Read(); k = Read();
		Put(solve(b,d) - solve(a-1,d) - solve(b,c-1) + solve(a-1,c-1),'\n');
	}
	return 0;
}