[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\)
代回原式
因为\(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)\)使其变得更大
当我们枚举\(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;
}