莫比乌斯反演(bzoj 2301: [HAOI2011]Problem b)

2301: [HAOI2011]Problem b

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


bzoj 1101一模一样的两道题,只不过x和y的范围不是从1开始的而已

理论容斥下就好了

bzoj 1101题解:http://blog.csdn.net/jaihk662/article/details/76392853

但1101题是用莫比乌斯函数+推论解决的

这里尝试用莫比乌斯反演解决,先拉出公式


其实这个公式有另一个表示形式


因为n固定,所以d表示为所有n的倍数(对的,是无穷无尽的,只不过F(d)可能在d超过某个数之后恒为0


那么这题我们可以

令f(k)为在1<=x<=n, 1<=x<=m且Gcd(x, y)==k的(x, y)对数(也就是答案)

令F(k)为在1<=x<=n, 1<=x<=m且k是Gcd(x, y)的约数的(x, y)对数

那么很显然有


套入莫比乌斯反演公式可得

(这里d只要大于min(n, m)式子就为0)

很显然我们只需要枚举小于等于min(n, m)的k的倍数就可以了

到这里说:题目和1101题一模一样,为什么公式不一样?

其实是一样的!对于这公式我们枚举的d是k的倍数,既然这样!那我们假设d = k*d'

那么原式有


转化成功!(注意这里和1101一样我们假设min(n, m)=n)

这样我们原本枚举的小于等于min(n, m)的k的倍数就等同于枚举小于等于min(n, m)的k的倍数再除以k

也就是枚举1到min(n, m)了!

用莫比乌斯反演转化成功

剩下就是和1101一样的两个程序了


#include<stdio.h>
#include<algorithm>
using namespace std;
int cnt, mu[50005] = {1,1}, flag[50005] = {1,1}, pri[50005], sum[50005];
int Jud(int n, int m)
{
	int L, R, ans;
	ans = 0;
	if(n>m)  swap(n, m);
	L = 1;
	while(L<=n)
	{
		R = min(n/(n/L), m/(m/L));
		ans += (sum[R]-sum[L-1])*(n/L)*(m/L);
		L = R+1;
	}
	return ans;
}
int main(void)
{
	int i, j, T, a, b, c, d, k;
	for(i=2;i<=50000;i++)
	{
		if(flag[i]==0)
		{
			pri[++cnt] = i;
			mu[i] = -1;
		}
		for(j=1;j<=cnt&&i*pri[j]<=50000;j++)
		{
			flag[i*pri[j]] = 1;
			if(i%pri[j]==0)
			{
				mu[i*pri[j]] = 0;
				break;
			}
			mu[i*pri[j]] = -mu[i];
		}
	}
	for(i=1;i<=50000;i++)
		sum[i] = sum[i-1]+mu[i];
	scanf("%d", &T);
	while(T--)
	{
		scanf("%d%d%d%d%d", &a, &b, &c, &d, &k);
		printf("%d\n", Jud(b/k, d/k)+Jud((a-1)/k, (c-1)/k)-Jud(b/k, (c-1)/k)-Jud((a-1)/k, d/k));
	}
	return 0;
}




发布了1121 篇原创文章 · 获赞 1380 · 访问量 51万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览