HDU-4135-Co-prime(容斥)

31 篇文章 0 订阅

HDU-4135-Co-prime

传送门

这道题是容斥~

题目大意:给你一个区间[a, b],和一个数n,让你求区间[a, b]范围内的与n互质的数有多少个。

解题思路:我们看看数据范围,a和b的区间范围达到了1e15,n的数据范围达到了1e9。t次询问范围在100以内。
我们把这道题的思路与前缀和结合起来想一想。求区间[a, b]与n互质的数字,那我们转化成求[1, b]区间与n互质的个数sum1,求[1, a - 1]区间与n互质的个数sum2。然后sum1 - sum2就是我们的区间[a, b]与n互质的个数了。
再把问题转化一下:我们求[1, x]区间与n不是互质的个数(因为求互质的话,是不是想到了欧拉函数,但是欧拉函数在这道题也不行)
不是互质的(gcd(k, n) != 1)
我们最普遍的方法就是找到n的质因数,求范围在[1, x]之间的质因数的倍数,这些倍数肯定是和n不是互质的。算出这些个数cnt之后。我们就可以用总数减去cnt,就可以得到我们的互质的个数啦~
举个例子:
当x = 20,n = 10;
n的质因数:2, 5;(任何数都可以用质因数的乘积表示,及质因数分解)
接下来我们算在区间[1, 20]范围内的质因数的倍数:
2的倍数:2,4,6,8,10,12,14,16,18,20;(10个即:20 / 2)
5的倍数:5,10,15,20;(4个即:20 / 5)
你想直接用10 + 4 = 14得到不是互质的个数吗,不,你错了,我们看上面很明显有重复的。
所以我们需要用到容斥~
我们把上面的倍数相应的减掉之后,需要加上重复减掉的数字,比如10,20;
这些数是2和5的乘积的倍数,即10的倍数。
如果设计到多个质因数,我们还可能遇到加多了的情况,所以我们要相应的减去。

即得到规律:
奇数个质因数的个数是要减去的,偶数个质因数的个数是要加上的
归结为:
在区间[1. x]中找到与n互质的数的个数的公式:(这里假设n的质因数有3个)
个数 = x - x / n的质因数1 - x / n的质因数2 - x / n的质因数3 + x / (n的质因数1n的质因数2)+ x / (n的质因数2n的质因数3)+ x / (n的质因数1n的质因数3)- x / (n的质因数1n的质因数2 * n的质因数3)
更多的质因数以此类推。

然后我们考虑一下求质因数的环节:
暴力肯定是不行了,预处理也不行。因为这个范围1e9,空间限制。
所以我们只有采用输入一个数n挨个挨个算出,这个数的质因数。这样不会tle。
这里求质因数直接用的埃筛哈~
cal(x, s)函数算的就是在区间[1, x]上与s互质的个数有多少。

好啦,我说完啦~

代码部分:

#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N = 1e5 + 10;

ll a, b, n;
ll ans;
int cnt;
vector<ll> p;
ll q[N];

void init(ll x)
{
	p.clear();
	for (ll j = 2; j * j <= x; j++)
	{
		if (!(x % j))
		{
			p.push_back(j);
			while (!(x % j))
			{
				x /= j;
			}
		}
	}
	if (x > 1)
	{
		p.push_back(x);
	}
}

ll cal(ll x, ll s)
{
	int num = 0;
	q[num++] = 1;
	int siz = p.size();
	for (int i = 0; i < siz; i++)
	{
		ll t = p[i];
		if (t > x)
		{
			break;
		}
		int k = num;
		for (int j = 0; j < k; j++)
		{
			q[num++] = q[j] * t * (-1);
		}
	}
	ll sum = 0;
	for (int i = 0; i < num; i++)
	{
		sum += x / q[i];
	}
	return sum;
}

int main()
{
	int t;
	scanf ("%d", &t);
	while (t--)
	{
		scanf ("%I64d%I64d%I64d", &a, &b, &n);
		init(n);
		ans = cal(b, n) - cal(a - 1, n);
		cout << "Case #" << ++cnt << ": ";
		cout << ans << endl;
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃酱斯密酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值