给定正整数 n≤109,需要构造出不超过 105 个真分数,满足:
- b1a1+b2a2+⋯+btat+n1=1.
- 对每个分数,bi<n 且 bi∣n.
若无解,输出 −1,否则给出一组可行解。
先将 n 质因数分解,分下列情况讨论。
首先考虑特殊情况。n=pm,其中 p 是质数。
要选一个因数作为分母,发现 n 的任意一个因数 b,都一定是 pa 的形式。
因为分母为 1 时不合题意,所以在 1 之外,我们任选 n 的两个因子,不妨设为 c,d,一定有 gcd(c,d)=1.
又因为 gcd(n−1,n)=1,而 gcd(c,d)=1。以 c,d 作为分母,一定不合法。
也就是说,当 n=pm 时,不存在合法解。
对于其他情况,n 一定可以被表示为两个互质的数的乘积。一种构造方法是:
将 n 质因数分解,不妨 n=p1m1p2m2⋯ptmt,令 x=p1m1,y=p2m2⋯ptmt,显然 xy=n 且 gcd(x,y)=1.
为了方便,我们钦定 x≤y。如果按照上面的方法得到的 x>y,交换 x,y 即可。
当 k=2 时,设题目所求的 b1a1=xc,b2a2=yd.
我们需要求出一组 (c,d) 满足 xc+yd=1−n1⇒xyc⋅y+d⋅x=nn−1.
因为 xy=n,这等价于满足:
c⋅y+d⋅x=n−1
继续整理,把 c⋅y 移到右侧,得到:
d=xn−1−cy
注意 c,d∈Z+。
先不考虑 d 的正负性。由于 x∣n,有:
d∈Z⇒x∣(n−1−c⋅y)⇒x∣(1+c⋅y)
想要找到满足 x∣(1+c⋅y) 的 c,考虑直接枚举。
注意到 gcd(x,y)=1。当 c 取 1,2,⋯x−1 时,都有 x∤(c⋅y),所以 c⋅y 的取值构成 modx 的完全剩余系。
也就是说,当 c 遍历 1∼x−1 时,c⋅ymodx 的取值两两不同,也恰好完全覆盖了 1∼x−1.
因此,一定存在 c∈[1,x−1] 满足 x∣(c⋅y+1).
再看 d 的正负性。由于 1+c⋅y≤1+(x−1)⋅y=1+xy−y=n−(y−1)<n,所以 n−(1⋅y)>0.
因此,一定有 d=xn−1−cy>0.
我们先按照 x=p1m1,y=p2m2⋯ptmt 的方法找到 (x,y).
之后,在范围 1∼x−1 中枚举 c,找到满足 x∣(c⋅y+1) 的 c,计算出 d=xn−1−cy。
(xc,yd) 即为满足题意的两个分数。
时间复杂度#
找到 (x,y) 的部分,需要对 x 分解质因数,时间复杂度为 O(n).
枚举 c 的部分,因为我们钦定了 x≤y,把 [1,x−1] 看作 x 个数,那么:
x=x⋅x≤x⋅y=n
所以总时间复杂度是 O(n) 的。
int n;
vector<int> fac;
int main() {
scanf("%d", &n);
int p1 = -1;
for(int i = 2; i * i <= n; i++) {
if(n % i == 0) {p1 = i; break;}
}
if(p1 == -1) p1 = n;
int x = p1, y = n / p1;
while(y % p1 == 0) y /= p1, x *= p1;
if(y == 1) {
cout << "NO" << endl;
return 0;
}
int ansc, ansd;
for(int c = 1; c <= x - 1; c++) {
if((c * y + 1) % x == 0) {
ansc = c; ansd = (n - 1 - c * y) / x;
break;
}
}
cout << "YES\n2\n" << ansc << " " << x << "\n" << ansd << " " << y << endl;
return 0;
}
cpp