题意
- 给我们三个1e7以内的数 c , d , x c, d, x c,d,x,要求我们求出有多少对 a , b a, b a,b,满足
c
⋅
l
c
m
(
a
,
b
)
−
d
⋅
g
c
d
(
a
,
b
)
=
x
c \cdot lcm(a, b) - d \cdot gcd(a, b) = x
c⋅lcm(a,b)−d⋅gcd(a,b)=x
思路
-
这里给出一种和题解不同(但本质上应该也是相似的)的一种解法。首先看到这个二元一次方程我们马上就可以想到扩展欧几里得。
-
这样我们用exgcd拿到关于lcm与gcd的正整数通解之后,还要考虑什么时候这两个数能组成一对真正的lcm与gcd
-
这里我们的通解是
l
c
m
=
X
+
n
∗
c
1
g
c
d
=
Y
+
n
∗
c
2
lcm = X + n * c_1 \\ gcd = Y + n * c_2
lcm=X+n∗c1gcd=Y+n∗c2
-
其中 c 1 c_1 c1是 d / g c d ( c , d ) d / gcd(c, d) d/gcd(c,d), c 2 c_2 c2是 c / g c d ( c , d ) c / gcd(c, d) c/gcd(c,d)
-
然后显然gcd必须整除lcm,这样才能找到a与b,这个可以写为
X
+
n
∗
c
1
Y
+
n
∗
c
2
=
k
(
k
∈
N
+
)
\frac{X + n * c_1}{Y + n * c_2} = k \quad (k \in N^+)
Y+n∗c2X+n∗c1=k(k∈N+)
- 可以写成下列二元二次方程的形式
X
+
n
∗
c
1
=
k
Y
+
k
∗
n
∗
c
2
X + n * c_1 = kY + k * n * c_2
X+n∗c1=kY+k∗n∗c2
- 显然找到这个方程的所有正整数解(n与k必须都是正整数)就能找到所有的a,b了,我们可以使用因式分解法来解决这个方程
(
c
2
∗
k
−
c
1
)
(
c
2
∗
n
+
Y
)
=
c
2
∗
x
−
c
1
∗
y
(c_2 * k - c_1)(c_2 * n + Y) = c_2 * x - c_1 * y
(c2∗k−c1)(c2∗n+Y)=c2∗x−c1∗y
-
方程右边是常数,所以我们对右边这个数因式分解,然后就能够得到左边的全部整数解,注意这个过程中要保证n与k都是正整数,否则跳过。
-
拿到了一对n和k,我们可以直接算出 l c m lcm lcm与 g c d gcd gcd,然后对于这一对 l c m lcm lcm与 g c d gcd gcd, ( a , b ) (a,b) (a,b)数对的数量其实就是 2 ( l c m / g c d ) 的 质 因 数 种 类 数 2^{(lcm / gcd) 的质因数种类数} 2(lcm/gcd)的质因数种类数,因为 l c m / g c d = = ( a / g c d ) ∗ ( b / g c d ) lcm / gcd == (a / gcd) * (b / gcd) lcm/gcd==(a/gcd)∗(b/gcd), 而我们知道 ( a / g c d ) (a / gcd) (a/gcd) 与 ( b / g c d ) (b / gcd) (b/gcd)是没有公因数的,所以也就是说将 l c m / g c d lcm / gcd lcm/gcd的几种不同的质因数进行任意分配,而每种质因数都可能被分到a或者b,所以对答案的贡献是
2
(
l
c
m
/
g
c
d
)
的
质
因
数
种
类
数
2^{(lcm / gcd) 的质因数种类数}
2(lcm/gcd)的质因数种类数
-
另外这个质因数种类数不能直接算,否则会t,不过我们可以线性预处理出2e7(注意上界是2e7不是1e7,可以从lcm的计算公式上得出)之内每个数的质因数种类数(记为 c n t i cnt_i cnti),具体方法是
-
对于当前 i i i,如果是质数则 c n t i = 1 cnt_i = 1 cnti=1
-
否则 c n t i = c n t i / m i n p r i m e i cnt_i = cnt_{i / minprime_i} cnti=cnti/minprimei, m i n p r i m e i minprime_i minprimei是指 i i i的最小质因数,另外,如果 i % m i n p r i m e i 2 ! = 0 i \% minprime_i^2 != 0 i%minprimei2!=0,还要让 c n t i + = 1 cnt_i += 1 cnti+=1,因为这代表 i i i 比 i / m i n p r i m e i i / minprime_i i/minprimei多了一个质因数 i i i
-
以上计算过程可以在欧拉筛的同时进行计算,因为欧拉筛使用到了最小质因数
-
AC代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
long long prime[20000005], co = 0;
bool iprime[20000005] = {0};
long long cntp[20000005] = {0};
long long qpow(long long a, long long b)
{
long long c = 1;
while (b)
{
if (b & 1)
{
c *= a;
}
a *= a;
b >>= 1;
}
return c;
}
void euler()
{
iprime[1] = true;
for (int i = 2; i <= 20000000; ++i)
{
if (!iprime[i])
{
prime[++co] = i;
cntp[i] = 1;
}
for (int j = 1; j <= co && i * prime[j] <= 20000000; ++j)
{
iprime[i * prime[j]] = true;
cntp[i * prime[j]] = cntp[i];
if (i % prime[j] == 0)
{
break;
}
++cntp[i * prime[j]];
}
}
}
long long gcd(long long a, long long b)
{
if (!b)
{
return a;
}
else
{
return gcd(b, a % b);
}
}
long long exgcd(long long a, long long b, long long& xx, long long& yy)
{
if (!b)
{
xx = 1;
yy = 0;
return a;
}
else
{
long long r = exgcd(b, a % b, xx, yy);
long long t = xx;
xx = yy;
yy = t - yy * (a / b);
return r;
}
}
long long calc(long long a, long long b, long long c)
{
long long xx, yy;
long long d = exgcd(a, b, xx, yy);
if (c - c / d * d == 0)
{
c /= d;
xx *= c;
b /= d;
if (b < 0)
{
b = -b;
}
xx %= b;
if (xx < 0)
{
xx += b;
}
return xx;
}
else
{
return -1;
}
}
int t;
long long c, d, x, ans;
int main()
{
euler();
scanf("%d", &t);
while (t--)
{
scanf("%lld%lld%lld", &c, &d, &x);
long long xx = calc(c, d, x);
if (xx == -1)
{
printf("0\n");
continue;
}
long long dd = gcd(c, d);
long long c1 = d / dd;
long long c2 = c / dd;
long long yy = -(x - xx * c) / d;
if (yy <= 0)
{
xx += c1 * (-yy / c2 + 1);
yy += c2 * (-yy / c2 + 1);
}
ans = 0;
long long p = xx * c2 - yy * c1;
for (long long i = 1; i * i <= p; ++i)
{
if (p % i == 0)
{
if (i >= yy && (i - yy) % c2 == 0 && (p / i + c1) % c2 == 0)
{
long long ln = (i - yy) / c2;
ans += qpow(2, cntp[(xx + ln * c1) / (yy + ln * c2)]);
}
if (i * i == p)
{
continue;
}
if (p / i >= yy && (p / i - yy) % c2 == 0 && (i + c1) % c2 == 0)
{
long long ln = (p / i - yy) / c2;
ans += qpow(2, cntp[(xx + ln * c1) / (yy + ln * c2)]);
}
}
}
printf("%lld\n", ans);
}
return 0;
}