一个整数可以分解为若干个质数的乘积,且这个分解是唯一的。
根据这个定理,可以得到整数的另一个定义:一个整数是一个由若干个质数构成的可重集合。
在只有乘除法的情况下,将整数的运算对应到集合的运算为:
1 <=> ∅ ; gcd(a,b) <=> a∩b ; lcm(a,b) <=> a∪b ; a/b <=> a-b ; a*b <=> a+b ; a|b <=> a为b的子集 ; ...
分析题目:
gcd(x,a0)=a1,lcm(x,b0)=b1
即x∩a0=a1,x∪b0=b1
由此可知:
1)a1为x的子集。
2)b1-b0为x的子集。
设k=a1∩(b1-b0) , a3=a1-k , b3=b1-b0-k
这三个部分是已知的,即x必须包含的,统称之为“必选区域”,分别称a1为“必选区域A”,b1-b0为“必选区域B”,a3为“必选区域a3”,b3为“必选区域b3”。
3)gcd(a0/a1,x)=1,即a0/a1是x除了必选区域A以外,不能包含的,称为不可选区域,设fbd=a0/a1。
4)x为b1的子集=>x-(b1-b0)为b0的子集,即x除了必选区域B外所拥有的元素只能从b0中选择,称之为“可选区域”。但这个可选区域还是初步的,可缩小范围的。
到现在为止已经得到了足够多的信息,可以判断是否无解:如果必选区域b3与不可选区域有交集则无解;如果初步可选区域中不含必选区域a3则无解。
现在根据之前的条件进一步缩小可选区域。最终会得到一个无约束的可选区域,x的所有可能性都由这个区域产生。
1)这个初步范围仅考虑了必选区域B,因此应再排除必选区域A的内容,其中AB可能有交集,因此排除必选区域a3即可。
2)lcm(x,b0)=b1
=> gcd(b0/gcd(x,b0) , x/gcd(x,b0))=1
=> gcd(b0/gcd(x,b0) , b1/b0)=1 (∵b1/b0 | x/gcd(x,b0))
gcd(x,b0)即为b0中所有与b1/b0拥有的元素相同的部分,也就是要排除的部分。
若这个部分与不可选区域有交集,则无解。 3)最后再从剩下的可选区域中排除不可选区域。
经过这3步,剩下来的可选区域为最终的自由选择区域,x的所有解的个数即这个区域的子集数目。但因为是可重集合,子集数目比较难求,又因为两个定义是等价的,子集数即整数的因数个数,所以我们转而求最终区域的因数个数。
只需要将它质因数分解:p1q1p2q2p3q3...pkqk
它的因子数目即(q1+1)(q2+1)(q3+1)...(qk+1)
我的参考代码如下,请参照题解阅读:
#include <stdio.h>
int su[5000] = {4, 2, 3, 5, 7}; //素数表
inline int gcd(int a, int b)
{
for (int c; c = a % b; a = b, b = c)
;
return b;
} //最大公约数
int main()
{
for (int i = 11; i < 45000; i++) //打素数表
{
int j;
for (j = 1; j <= su[0] && su[j] * su[j] <= i && i % su[j]; j++)
;
if (su[j] * su[j] > i || j > su[0])
su[++su[0]] = i;
}
int n;
for (scanf("%d", &n); n > 0; n--)
{
int a0, a1, b0, b1;
scanf("%d %d %d %d", &a0, &a1, &b0, &b1);
int k = gcd(b1 / b0, a1); //必选区域A,B的交集
int a3 = a1 / k, b3 = b1 / b0 / k, fbd = a0 / a1;
if (gcd(b3, fbd) > 1 || b0 % a3 != 0)
printf("0\n"); //如果必选区域b3与禁止选的区域有交集则无解,如果可选区域中没有必选区域a3也无解
else
{
int t = b0 / a3; //1)排除a3
if (gcd(fbd, gcd(t, k)) > 1)
printf("0\n"); //如果可选区域中残留的必选区域和禁止选的区域有交集则无解。因为b3与fbd必无交集,此处不需考虑
else
{
int ans = 1;
for (int c; (c = gcd(t, b3\* k)) > 1; t /= c)
; //2)从可选区域中排除残余必选区域
for (int c; (c = gcd(t, fbd)) > 1; t /= c)
; //3)从可选区域中排除禁止区域
for (int i = 1, j; i <= su[0] && su[i] <= t; ans\*= j, i++) //对可选区域分解质因数,并计算因子数目
for (j = 1; t % su[i] == 0; j++, t /= su[i])
;
printf("%d\n", ans << (t > 1)); //如果最后t不为1,说明含有一个大素数
}
}
}
return 0;
}