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})
( herefore 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
", ans);
}
}
int main()
{
freopen("in.in", "r", stdin);
get_prime();
get_two();//预处理2的阶乘
work();
return 0;
}