CodeTON Round 3 (Div. 1 + Div. 2, Rated, Prizes!)1750D. Count GCD 容斥,分解质因数板子看k内与a互质的数目

本文介绍了解决Codeforces问题中关于给定数组a,计算满足前i个数GCD等于ai的相同长度数组b的数量。通过质因数分解和容斥原理计算互质数,给出了参考代码实现。
摘要由CSDN通过智能技术生成

Problem - 1750D - Codeforces

分解质因数板子:

分解质因数板子-CSDN博客

目录

题意:

分析:

参考代码:


题意:

给你个数组a,看你能找出多少种长度相同的数组b。

条件是前i个数的gcd等于ai

分析:

前 i-1个数的gcd就是ai-1了。

所以每个位置之间独立,即我们最终结果看每个位置有多少种,乘起来即可。

gcd (ai-1,bi) == ai   ——> gcd ( ai-1/ai  , bi/ai ) == 1

ai-1 / ai 已知。

我们要看bi有多少种,也就是看1 ~ bi/ai 有多少个符合。

设bi / ai 为 k,则

也就是看 1~k 有多少个与 ai-1 / ai 互质。

我们将ai - 1 / ai质因数分解来计算:

((最小的9个质因子)2*3*5*7*11*13*17*19*23 == 223092870 < 1e9)

含有质因子就不互质,总数k减去不互质的数目就是互质的数目。

(比如 c 与 ai-1 / ai 互质 ,那么2*c , 3*c也是互质的,所以k内就有k/c个。)

由于我们计算数目的时候是以↑的方式计算的,所以为了避免重复计算(比如质因子含有2的,和含有3的,以及同时含有2和3的),才需要用容斥

说到这里应该很明确了。

参考代码:


long long fp(long long a, int c)
{
	if (c == 0)return 1;
	if (c == 1)return a % MOD;
	long long tmp = fp(a, c / 2);
	if (c % 2 == 0)
		return tmp * tmp % MOD;
	else
		return tmp * tmp % MOD * a % MOD;
}


vector<int>divide(int x)
{
	vector<int>ret;
	for (int i = 2; i <= x / i; i++)//i*i <= x 
	{
		if (x % i == 0)
		{
			ret.push_back(i);
			while (x % i == 0)
				x /= i;
		}
	}
	if (x > 1)ret.push_back(x);
	return ret;
}
void solve()
{
	int n, m;
	cin >> n >> m;
	vector<int>arr(n + 1);
	for (int i = 1; i <= n; i++)
	{
		cin >> arr[i];
	}
	ll ans = 1;
	for (int i = 2; i <= n; i++)
	{
		if (arr[i - 1] % arr[i] != 0)
		{
			ans = 0; break;
		}
		else
		{
			//求一个范围内,与数v互素的个数,我们可以用容斥来解决
			int aim1 = arr[i - 1] /arr[i];
			vector<int>ret = divide(aim1);
			int k = m / arr[i];
			int situ = 0;
			for (int i = 1; i < fp(2,ret.size()); i++)
			{
				int multi = 1;
				int cnt = 0;			
				for (int j = 1, cur = 1; j <= ret.size(); j++,cur<<=1)
				{
					if (cur & i)
					{
						multi *= ret[j-1];
						cnt++;
					}
				}
				if (cnt % 2)
					situ += k / multi;
				else
					situ -= k / multi;
			}
			ans = ans * (k-situ) % MOD;
		}
	}
	cout << ans << endl;
}
signed main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	int t = 1;
	cin >> t;
	while (t--)
	{
		solve();
	}
	return 0;
}

  • 20
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值