2301: [HAOI2011]Problem b

2301: [HAOI2011]Problem b

Time Limit: 50 Sec  Memory Limit: 256 MB
Submit: 3473  Solved: 1565
[ 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

Source

[ Submit][ Status][ Discuss]

理论了n天,,终于在忘得差不多的时候写了这道莫比乌斯反演
首先根据能量采集那题,可以写出一个单次O(nlogn)的算法,不过,GG
考虑容斥原理
f[k]:∑u[d/k]*[n/d]*[m/d]  d是k的整数倍
这样我们就得到一个单词询问O(n)的算法
但是,还是GG
实际上把式子拆一拆
[n/d]的值不超过2根号n种,[m/d]的值不超过2根号m种
所以,[n/d]*[m/d]的值不超过2根号n + 2根号m种
枚举乘法答案,运用莫比乌斯函数前缀和,完了
详见代码
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;

typedef long long LL;
const int maxn = 5E4 + 50;

LL ans,mu[maxn];
int a,b,c,d,k,T;
bool not_prime[maxn];

LL tot(LL n,LL m,LL k)
{
	n /= k; m /= k;
	int last,t = min(n,m);
	LL ret = 0;
	for (LL i = 1; i <= t; i = last + 1) {
		last = min(n/(n/i),m/(m/i));
		ret += (n/i)*(m/i)*(mu[last] - mu[i-1]);
	}
	return ret;
} 

int main()
{
	#ifdef DMC
		   freopen("DMC.txt","r",stdin);
	#endif
	
	for (int i = 1; i < maxn; i++) mu[i] = 1;
	for (int i = 2; i < maxn; i++)
		if (!not_prime[i]) {
			mu[i] = -1;
			for (int j = 2; j*i < maxn; j++) {
				not_prime[j*i] = 1;
 				mu[j*i] *= mu[i];
				if (j % i == 0) mu[j*i] = 0;
			}
		} 
	for (int i = 1; i < maxn; i++) mu[i] += mu[i-1];
		
	cin >> T;
	while (T--) {
		scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
		printf("%lld\n",tot(b,d,k) - tot(a - 1,d,k) - tot(b,c - 1,k) + tot(a - 1,c - 1,k));
	}
	return 0;
}


你可能感兴趣的:(莫比乌斯反演)