题意简述
给定一个 n n n,确定一些分数,使得这些分数的分母是 n n n的因数,且 < n <n <n,并且这些分数都是真分数,这些分数的和+ 1 n = 1 \frac{1}{n}=1 n1=1。
数据
输入
只有一行,是一个正整数 n n n
输出
如果无解输出一行"NO"(没有引号)
如果有解,先输出"YES"(没有引号),然后输出一个
k
k
k,表示有多少个分数。接下来
k
k
k行,每行两个数,是一个经过化简的分数,第一个是分子,第二个是分母。注意您输出的
k
k
k要
<
=
1
0
5
<=10^5
<=105,因为数据保证一定在
1
0
5
10^5
105长度内有解。如果您输出的
k
k
k超过了
1
0
5
10^5
105,就会被判错(具体是
O
L
E
OLE
OLE还是
W
A
WA
WA不知道,反正都是错了)。
数据
输入
2
输出
NO
输入
6
输出
YES
2
1 2
1 3
思路
这个题也是。。。无话珂说。。。
正式开始。首先我们要换一下思维方式,相当于凑几个分数使得和是 n − 1 n \frac{n-1}{n} nn−1,由于要求分母都是 b b b的因数,我们不妨将这些分数都通分,就又变成:将 n − 1 n-1 n−1分成一些不和 n n n互质的数之和。
举个栗子:输入 6 6 6,我们就把 5 5 5分成 2 2 2+ 3 3 3,接下来就珂以轻易得到: 5 6 = 2 + 3 6 = 2 6 + 3 6 = 1 3 + 1 2 \frac{5}{6}=\frac{2+3}{6}=\frac{2}{6}+\frac{3}{6}=\frac{1}{3}+\frac{1}{2} 65=62+3=62+63=31+21
这样看起来就珂以跑背包了。。。但是注意到,不和 n n n互质的数有很多,基本上和 n n n同级,但是 n n n是 1 e 9 1e9 1e9的,然后。。。根本跑不动,直接就炸空间了。。。
虽然跑不动背包,但是这个想法看起来很好,不如我们把它 烤了吃了 优化一下。我们发现,所有不和
n
n
n互质的数至少是
n
n
n的一个质因数的倍数。然后我们珂以把这些不和
n
n
n互质的大数拆成某个质因数的几倍。(比如说输入
100
100
100,其中一个不和
100
100
100互质的数是
98
98
98,
100
100
100的质因数有
2
,
5
2,5
2,5,所以我们就珂以把它拆成
49
49
49个
2
2
2)。这样虽然量多了一点,但至少空间能过了。不过仿佛不太能保证
k
k
k在
1
0
5
10^5
105以内。。。
所以要减少数量。我们会发现,一个大的质数 k k k一定珂以用两个小的质数 a , b a,b a,b通过叠加凑出来(没有说分数一定要不一样哦~)。
简要证明一下。因为 a , b , k a,b,k a,b,k都是质数,所以肯定是两两互质的。然后相当于解方程 a x + b y = k ax+by=k ax+by=k,有解的条件是 k k k是 g c d ( a , b ) gcd(a,b) gcd(a,b)的倍数。由于 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1,所以 k k k是 g c d ( a , b ) gcd(a,b) gcd(a,b)的倍数,所以肯定是有解的。
有了这个之后,我们考虑用 n n n最小的两个质因数去凑所有的答案。
(比如输入 n = 100 n=100 n=100,最小的是 2 2 2和 5 5 5,我们就用 2 2 2和 5 5 5去凑 99 99 99。容易看出一个解: 5 5 5用 19 19 19个, 2 2 2用两个。)
这一步珂以用 e x g c d exgcd exgcd得到(注意要配正整数,是求正整数解,因为一个分数肯定不能选负数个)。由于我的 e x g c d exgcd exgcd解这个写的比较奇怪,如果看不懂代码中的 c a l c calc calc函数,请看下面的解释:
当 a , b a,b a,b均为质数的时候,我们会用 e x g c d exgcd exgcd求解 a x + b y = 1 ax+by=1 ax+by=1,把 x , y x,y x,y乘以 k k k,就珂以得到 a x + b y = k ax+by=k ax+by=k的解了。但是显然我们这里要求正整数解,所以我们接下来的一个问题就是要把 x , y x,y x,y配成正整数。如果两个都正了,就直接返回;否则不珂能两个都是负的,要不然加起来 k k k也是负的了。所以肯定是一个正,一个负。不妨令 y y y是正的那个(如果 y y y是负的,就换一下 x , y x,y x,y,最后返回的时候再换回来,这个好好想一想就明白了)。然后我们会发现,如果 x , y x,y x,y是一个满足条件的解,那么 x + b , y − a x+b,y-a x+b,y−a也是一个满足条件的解(代入一下会发现两个 a b ab ab抵消了)。那么,我们只需要将 y y y不断的 − a -a −a, x x x不断的 + b +b +b,就珂以把 x , y x,y x,y配成正的。当然,我们显然不能暴力一个一个加上去,所以我这里用了一个 c n t cnt cnt,表示我们需要加多少次。加多少次么。。。我们肯定不能让 y y y给变负的了,所以我们最多就是减到 y < a y<a y<a为止(也就是 ⌊ y a ⌋ \lfloor\frac{y}{a}\rfloor ⌊ay⌋次)为止,此时 x x x如果还是负的,我们也就没办法了,因为我们已经尽量多的加了。事实证明没有这样的情况。。。
然后这样我们就求出来了一个正整数解。我们珂能会想:这不是负优化了么?艹nm明明更多了好么?此时我们不要急,回到原来的问题上,然后扪心自问一下:
我们完成了什么?
“设 a , b a,b a,b是 n n n的最小的两个质因数,我们已经能够将 n − 1 n-1 n−1写成 a x + b y ax+by ax+by的形式。”
此时,我们会发现,出题人给我们开了一个大玩笑,我们 只 要 输 出 a x n + b y n \color{red}只要输出\frac{ax}{n}+\frac{by}{n} 只要输出nax+nby即珂。( a x ax ax和 b y by by因为分别有 a , b a,b a,b,所以显然和 n n n不互质)。这个分数约分之后至少能够约成 x n / a + y n / b \frac{x}{n/a}+\frac{y}{n/b} n/ax+n/by的形式,明显是满足条件的。也就是说 k < = 1 0 5 k<=10^5 k<=105是吓人的,只要 k = 2 k=2 k=2就珂以做了!!!
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define int long long
int n;
void exgcd(int a,int b,int &x,int &y)//拓展欧几里得
{
if (b==0)
{
x=1,y=0;return;
}
else
{
int x_,y_;
exgcd(b,a%b,x_,y_);
x=y_;
y=x_-a/b*y_;
return;
}
}
pair<int,int> calc(int a,int b,int k)//解方程ax+by=k,并将结果打到pair里
{
int x,y;
exgcd(a,b,x,y);
x*=k;
y*=k;
if (x>=0 and y>=0)
{
return make_pair(x,y);
}
bool flag=0;
if (x>=0 and y<0)
{
swap(x,y);
swap(a,b);
flag=1;
}
int cnt=y/a;
y=y-cnt*a;
x=x+cnt*b;
return flag?make_pair(y,x):make_pair(x,y);
//上面解释过这个奇怪的写法
}
int d[100],p[100];
void Decompose(int x)
//这一步一定要保证是O(根号n)的!!!
//我写了个线性的,加上各种玄学优化才过。。。
{
int cnt=0;
for(int i=2;i*i<=x;++i)
{
if (x%i==0)
{
++cnt;
d[cnt]=i;p[cnt]=0;
while(x%i==0)
{
++p[cnt];
x/=i;
}
}
}
if (x!=1)
{
++cnt;
d[cnt]=x,p[cnt]=1;
}
return;
}
void putfrac(int a,int b)//约分后输出一个分数
{
int g=__gcd(a,b);
printf("%I64d %I64d\n",a/g,b/g);
}
void Solve()
{
Decompose(n);
if (d[2]==0 and p[2]==0)//这说明n没有两个质因数,所以也就无解了。。。
{
puts("NO");return;
}
int a=d[1],b=d[2];//取最小的两个
pair<int,int>tmp=calc(a,b,n-1);
int x=tmp.first,y=tmp.second;//获取解
puts("YES\n2");
putfrac(a*x,n);
putfrac(b*y,n);//输出ax/n和by/n
}
void Main()
{
if (0)
{
freopen("","r",stdin);
freopen("","w",stdout);
}
scanf("%I64d",&n);
Solve();
}
#undef int //long long
};
main()
{
Flandle_Scarlet::Main();
return 0;
}