bzoj 2301 (莫比烏斯反演)

題目鏈接:https://www.lydsy.com/JudgeOnline/problem.php?id=2301

2301: [HAOI2011]Problem b
Time Limit: 50 Sec Memory Limit: 256 MB
Submit: 8632 Solved: 4233
[Submit][Status][Discuss]
Description

對於給出的n個詢問,每次求有多少個數對(x,y),滿足a≤x≤b,c≤y≤d,且gcd(x,y) = k,gcd(x,y)函數爲x和y的最大公約數。

Input

第一行一個整數n,接下來n行每行五個整數,分別表示a、b、c、d、k

Output

共n行,每行一個整數表示滿足要求的數對(x,y)的個數

Sample Input
2

2 5 1 5 1

1 5 1 5 2

Sample Output

14

3

HINT

100%的數據滿足:1≤n≤50000,1≤a≤b≤50000,1≤c≤d≤50000,1≤k≤50000


分析:

與hdu 1695題解類似,多了個容斥

代碼:

#include <bits/stdc++.h>

using namespace std;
#define ll long long 
#define fr first
#define sc second

const int N=5e4+5;
int prime[N+1];
int mob[N+1];
ll sum[N+1];

void Mobius()
{
	memset(prime,0,sizeof(prime));
	mob[1]=1;
	sum[1]=1;
	for(int i=2;i<=N;i++)
	{
		if(!prime[i])
		{
			prime[++prime[0]]=i;
			mob[i]=-1;
		}
		for(int j=1;j<=prime[0]&&i*prime[j]<=N;j++)
		{
			prime[i*prime[j]]=1;
			if(i%prime[j]==0)
			{
				mob[i*prime[j]]=0;
				break;
			}
			mob[i*prime[j]]=-mob[i];
		}
		sum[i]=sum[i-1]+mob[i];
	}
}

ll solve(int a,int b)
{
	ll ans=0;
	int tmp = min(a,b);
	
	for(int l=1,r;l<=tmp;l=r+1)
	{
		r=min(a/(a/l),b/(b/l));// 對於兩個變量,還可以這樣分塊,學到了
		ans += 1LL*(sum[r]-sum[l-1])*(a/l)*(b/l);
	}
	return ans;
}


int main()
{
	Mobius();
	int t,a , b, c,d,k;
	ll ans1,ans2,ans3,ans4;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
		if(k==0) {printf("0\n");continue;}
		ans1=ans2=ans3=ans4=0;
		a--;
		c--;
		a/=k,b/=k,c/=k,d/=k;
		ans1=solve(b,d);
		ans2=solve(a,d);
		ans3=solve(b,c);
		ans4=solve(a,c);
		
		
		printf("%lld\n",ans1-ans2-ans3+ans4);
		
	}

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