传送门
题目大意:
给定 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•lcm(a,b)-d•gcd(a,b)=x c•lcm(a,b)−d•gcd(a,b)=x
解题思路:
c • l c m ( a , b ) − d • g c d ( a , b ) = x ; l c m ( a , b ) = a • b g c d ( a , b ) ; c • a • b g c d ( a , b ) − d • g c d ( a , b ) = x ; c•lcm(a,b)-d•gcd(a,b)=x;\\ lcm(a,b)=\frac{a•b}{gcd(a,b)};\\ \frac{c•a•b}{gcd(a,b)}-d•gcd(a,b)=x; c•lcm(a,b)−d•gcd(a,b)=x;lcm(a,b)=gcd(a,b)a•b;gcd(a,b)c•a•b−d•gcd(a,b)=x;
令 g = g c d ( a , b ) g = gcd(a,b) g=gcd(a,b),则有 a = k 1 • g a=k_1•g a=k1•g, b = k 2 • g b=k_2•g b=k2•g;
c • k 1 • k 2 • g − d • g = x ; g • ( c • k 1 • k 2 − d ) = x ; g = x c • k 1 • k 2 − d ; c•k_1•k_2•g-d•g=x;\\ g•(c•k_1•k_2-d)=x;\\ g=\frac{x}{c•k_1•k_2-d}; c•k1•k2•g−d•g=x;g•(c•k1•k2−d)=x;g=c•k1•k2−dx;
其中 g ∈ N + g∈N^+ g∈N+,即 ( c • k 1 • k 2 − d ) ∣ x ; (c•k_1•k_2-d)\ |\ x; (c•k1•k2−d) ∣ x; (符号解释: x ∣ y x|y x∣y 指的是 x x x 可以整除 y y y,即 y y y % x = = 0 x==0 x==0)
由此可以想到枚举 x x x 的约数来求解;假设其中的一个约数为 y y y;
y = c • k 1 • k 2 − d ; k 1 • k 2 = y + d c ; y=c•k_1•k_2-d;\\ k_1•k_2=\frac{y+d}{c}; y=c•k1•k2−d;k1•k2=cy+d;
在约数 y y y 确定的情况下, g g g的值也是确定的,因 a = k 1 • g a=k_1•g a=k1•g, b = k 2 • g b=k_2•g b=k2•g,所以 k 1 k_1 k1 与 k 2 k_2 k2 的值和 a 与 b a与b a与b的值是一 一对应的,因此可通过计算 ( k 1 , k 2 ) (k_1,k_2) (k1,k2)的对数来间接计算 ( a , b ) (a,b) (a,b)的对数。
g c d ( a , b ) = g gcd(a,b)=g gcd(a,b)=g, k 1 k_1 k1与 k 2 k_2 k2互质。令 m = y + d c m=\frac{y+d}{c} m=cy+d ;
问题: 需要求有多少对互质的正整数的乘积是 m m m?
算术基本定理:任何一个大于1的自然数 N N N, 如果 N N N 不为质数,那么 N N N 可以唯一分解成有限个质数的乘积。
则 m m m 可以表示为: m = p 1 c 1 • p 2 c 2 • . . . • p n c n m=p_1^{c_1}•p_2^{c_2}•...•p_n^{c_n} m=p1c1•p2c2•...•pncn
任意两个质数之间是互质的, 1 1 1与任意的正整数都是互质的。
很明显相同的质因子不能同时出现在 k 1 k_1 k1 和 k 2 k_2 k2 中,则可按照质因数种类数将其化分为 n n n 类,对于每一类数要么全部放入 k 1 k_1 k1中,要么全部放入 k 2 k_2 k2中。因质数之间互质,固质数的幂之间同样是互质的,所以以这种形式划分是合理的。每一类都有两种选择,一共有 n n n类,所以总的方案数为 2 n 2^n 2n种。
综上所述,本题的答案即为: a n s w e r = ∑ y ∣ x g e t ( y ) answer=\displaystyle\sum_{y|x}{get(y)} answer=y∣x∑get(y).
g e t 函 数 get函数 get函数:传入参数 y y y ,计算出 y + d c \frac{y+d}{c} cy+d的质因子个数 c n t cnt cnt,返回 2 c n t 2^{cnt} 2cnt;
分析一下时间复杂度,线性筛O(n),枚举约数 n \sqrt{n} n,t次询问, g e t get get函数O( n l o g n \sqrt{\frac{n}{log\ n}} log nn),总的时间复杂度O( n + t n+t n+t• n \sqrt{n} n• n l o g n \sqrt{\frac{n}{log\ n}} log nn), t ∈ 1 e 4. n ∈ 1 e 7 t∈1e4.n∈1e7 t∈1e4.n∈1e7,很明显时间复杂度很大。接下来考虑优化。
每一个数的质因子个数可以在线性筛中维护,具体维护方法请参考代码。因此get函数即可O(1)的时间复杂度来实现。
总的时间复杂度O( n + t • n n+t•\sqrt{n} n+t•n),大约是O(4e7),在2s的时间限制内完全可以过掉。
上代码:
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N = 20000010;
ll c, d, x;
ll prime[N], cnt;
ll vis[N];
void get_prime()
{
for (int i = 2;i < N;i++)
{
if (!vis[i]) prime[cnt++] = i, vis[i] = 1;
for (int j = 0;prime[j] <= N / i;j++)
{
if (i % prime[j] == 0)
{
vis[prime[j] * i] = vis[i];
break;
}
else {
vis[prime[j] * i] = vis[i] + 1;
}
}
}
}
ll qpow(ll a, ll b)
{
ll res = 1;
while (b)
{
if (b & 1) res = res * a;
a = a * a;
b >>= 1;
}
return res;
}
ll get(ll i)
{
if ((i + d) % c) return 0;
int m = (i + d) / c;
return qpow(2, vis[m]);
}
int main()
{
int t;cin >> t;
get_prime();
while (t--)
{
cin >> c >> d >> x;
ll ans = 0;
for (int i = 1;i <= x / i;i++)
{
if (x % i == 0)
{
ans += get(i);
if (x / i != i) ans += get(x / i);
}
}
cout << ans << endl;
}
return 0;
}
参考文献