[CF1499D]The Number of Pairs 数学

D. The Number of Pairs

题意

给你\(c, d, x\),求满足\(c \cdot lcm(a, b) - d \cdot gcd(a, b) = x\)的点对\((a, b)\)的个数。
\(T\)组数据
\(T <= 10^4\), \(c, d, x <= 10^7\)

题解

为了表示简单一点,令\(l = lcm(a, b), g = gcd(a, b)\)
\(cl - dg = x\)
由一些常识,我们知道\(lcm(a, b) \cdot gcd(a, b) = a \cdot b\)
所以\(l = \frac{ab}{g}\)
\(\therefore \frac{cab}{g} - dg = x\)
因为\(g\)\(gcd(a, b)\),所以可设\(a = kg, b = tg\)
代回原式

\[cktg - dg = x \]

\[g = \frac{x}{ckt - d} \]

因为\(g\)是整数,所以\(ckt - d\)\(x\)的因子
所以我们枚举\(i = ckt - d\)
易知对于一个已知的\(g\)而言,\((k, t)\)\((a, b)\)是一一对应的关系
所以我们要统计\((a, b)\)相当于统计\((k, t)\)
又因为\(gcd(a, b)\)是最大公约数,所以\(k, t\)必互质,否则就可以将\(gcd(k, t)\)加入\(gcd(a, b)\)使其变得更大

\[\because i = ckt - d \]

\[\therefore kt = \frac{i + d}{c} \]

当我们枚举\(i\)时,\(\frac{i + d}{c}\)为常量
所以我们就是对于一个常量找互质的数对\((k, t)\)使其满足数对乘积等于这个常量
相当于对这个常量质因数分解,把分解出来的质因数分成两份,找合法的分配方案数
\(\frac{i + d}{c} = \prod_{j<=m}{p_j^{f_j}}\), \(m\)\(\frac{i + d}{c}\)的质因数种数,\(f_j\)表示\(p_j\)这个质因数有\(f_j\)
那么因为分出来的两部分互质,所以对于一个质因数\(p_j\)要么全给\(k\)要么全给\(t\)
所以由一些数学常识(对于每个质因数要么选要么不选),我们可以知道,方案数为\(2^m\)
所以\((k, t)\)的对数就是\(\sum_{i | k}{2^{m(i)}}\)
对于每组询问,我们暴力枚举\(x\)的因子\(i\), 对于每个\(i\),我们找出\(\frac{i + d}{c}\)对应的质因数种数\(m(\frac{i + d}{c})\)
统计一下\(\sum{2^{m(\frac{i + d}{c})}}\)就是答案
估计一下,因子\(i\)最多log个,\(m(\frac{i + d}{c}) <= log (\frac{i + d}{c})\)
\(\sum{2^{m(\frac{i + d}{c})}}\)大概不会超过\(\frac{i + d}{c} \cdot i\)个,也就差不多是\(10^{14}\)这个级别,用longlong应该就够了

\(m(i)\)可以欧拉筛预处理一下搞搞,2的幂也可以预处理一下
复杂度\(O(N + T \cdot \sqrt{N})\)

代码…………
还没写……鸽王天天鸽

---update in 21.5.7---
代码来了!

#include<bits/stdc++.h>
using namespace std;
#define R register int 
#define LL long long 
#define AC 14000000
#define ac 20000000 + 3

int T, tot;
LL c, d, x, ans;
int pri[AC], m[ac];
bool z[ac];
LL two[80];

inline int read()
{
	int x = 0; char c = getchar(); bool z = 0;
	while((c > '9' || c < '0') && c != '-') c = getchar();
	if(c == '-') z = 1, c = getchar();
	while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
	return z ? -x : x; 
}

void get_prime()
{
	z[1] = 1;
	for(R i = 2; i <= 20000000; i ++)
	{
		if(!z[i]) pri[++ tot] = i, m[i] = 1;//不要忽略i本身是质数时m(i) = 1
		for(R j = 1; j <= tot && i * pri[j] <= 20000000; j ++)
		{
			z[i * pri[j]] = 1, m[i * pri[j]] = m[i] + 1;
			if(!(i % pri[j])) 
			{
				m[i * pri[j]] --;//如果i * pri[j]剩下那个因子i里也有pri[j]这个因子,那就不用重复统计了
				break;
			}
		}
	}
//	printf("%d", tot);
}

void get_two()//才几十不用快速幂
{
	two[0] = 1;
	for(R i = 1; i <= 63; i ++) two[i] = two[i - 1] << 1;
}

void work()
{
	T = read();
	while(T --)
	{
		c = read(), d = read(), x = read();
		int t = sqrt(x), now;
		ans = 0;
		for(R i = 1; i <= t; i ++)
		{
			if(x % i) continue;//首先要是x的因子
			if(!((i + d) % c))// continue; 这里不能直接continue,因为后面那个可能是可以算的阿
			{
				now = (i + d) / c;
				ans += two[m[now]];//注意now的范围可以很大
			}
			if((x / i + d) % c || i * i == x) continue;
			now = (x / i + d) / c;
			ans += two[m[now]];
		}
		printf("%lld\n", ans);
	}
}

int main()
{
	freopen("in.in", "r", stdin);
	get_prime();
	get_two();//预处理2的阶乘
	work();
	return 0;
}
posted @ 2021-05-03 19:15  ww3113306  阅读(64)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。