题意简述
给定一个nnn,确定一些分数,使得这些分数的分母是nnn的因数,且<n<n<n,并且这些分数都是真分数,这些分数的和+1n=1\frac{1}{n}=1n1=1。
数据
输入
只有一行,是一个正整数nnn
输出
如果无解输出一行"NO"(没有引号)
如果有解,先输出"YES"(没有引号),然后输出一个kkk,表示有多少个分数。接下来kkk行,每行两个数,是一个经过化简的分数,第一个是分子,第二个是分母。注意您输出的kkk要<=105<=10^5<=105,因为数据保证一定在10510^5105长度内有解。如果您输出的kkk超过了10510^5105,就会被判错(具体是OLEOLEOLE还是WAWAWA不知道,反正都是错了)。
数据
输入
2
输出
NO
输入
6
输出
YES
2
1 2
1 3
思路
这个题也是。。。无话珂说。。。
正式开始。首先我们要换一下思维方式,相当于凑几个分数使得和是n−1n\frac{n-1}{n}nn−1,由于要求分母都是bbb的因数,我们不妨将这些分数都通分,就又变成:将n−1n-1n−1分成一些不和nnn互质的数之和。
举个栗子:输入666,我们就把555分成222+333,接下来就珂以轻易得到:56=2+36=26+36=13+12\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
这样看起来就珂以跑背包了。。。但是注意到,不和nnn互质的数有很多,基本上和nnn同级,但是nnn是1e91e91e9的,然后。。。根本跑不动,直接就炸空间了。。。
虽然跑不动背包,但是这个想法看起来很好,不如我们把它 烤了吃了 优化一下。我们发现,所有不和nnn互质的数至少是nnn的一个质因数的倍数。然后我们珂以把这些不和nnn互质的大数拆成某个质因数的几倍。(比如说输入100100100,其中一个不和100100100互质的数是989898,100100100的质因数有2,52,52,5,所以我们就珂以把它拆成494949个222)。这样虽然量多了一点,但至少空间能过了。不过仿佛不太能保证kkk在10510^5105以内。。。
所以要减少数量。我们会发现,一个大的质数kkk一定珂以用两个小的质数a,ba,ba,b通过叠加凑出来(没有说分数一定要不一样哦~)。
简要证明一下。因为a,b,ka,b,ka,b,k都是质数,所以肯定是两两互质的。然后相当于解方程ax+by=kax+by=kax+by=k,有解的条件是kkk是gcd(a,b)gcd(a,b)gcd(a,b)的倍数。由于gcd(a,b)=1gcd(a,b)=1gcd(a,b)=1,所以kkk是gcd(a,b)gcd(a,b)gcd(a,b)的倍数,所以肯定是有解的。
有了这个之后,我们考虑用nnn最小的两个质因数去凑所有的答案。
(比如输入n=100n=100n=100,最小的是222和555,我们就用222和555去凑999999。容易看出一个解:555用191919个,222用两个。)
这一步珂以用exgcdexgcdexgcd得到(注意要配正整数,是求正整数解,因为一个分数肯定不能选负数个)。由于我的exgcdexgcdexgcd解这个写的比较奇怪,如果看不懂代码中的calccalccalc函数,请看下面的解释:
当a,ba,ba,b均为质数的时候,我们会用exgcdexgcdexgcd求解ax+by=1ax+by=1ax+by=1,把x,yx,yx,y乘以kkk,就珂以得到ax+by=kax+by=kax+by=k的解了。但是显然我们这里要求正整数解,所以我们接下来的一个问题就是要把x,yx,yx,y配成正整数。如果两个都正了,就直接返回;否则不珂能两个都是负的,要不然加起来kkk也是负的了。所以肯定是一个正,一个负。不妨令yyy是正的那个(如果yyy是负的,就换一下x,yx,yx,y,最后返回的时候再换回来,这个好好想一想就明白了)。然后我们会发现,如果x,yx,yx,y是一个满足条件的解,那么x+b,y−ax+b,y-ax+b,y−a也是一个满足条件的解(代入一下会发现两个ababab抵消了)。那么,我们只需要将yyy不断的−a-a−a,xxx不断的+b+b+b,就珂以把x,yx,yx,y配成正的。当然,我们显然不能暴力一个一个加上去,所以我这里用了一个cntcntcnt,表示我们需要加多少次。加多少次么。。。我们肯定不能让yyy给变负的了,所以我们最多就是减到y<ay<ay<a为止(也就是⌊ya⌋\lfloor\frac{y}{a}\rfloor⌊ay⌋次)为止,此时xxx如果还是负的,我们也就没办法了,因为我们已经尽量多的加了。事实证明没有这样的情况。。。
然后这样我们就求出来了一个正整数解。我们珂能会想:这不是负优化了么?艹nm明明更多了好么?此时我们不要急,回到原来的问题上,然后扪心自问一下:
我们完成了什么?
“设a,ba,ba,b是nnn的最小的两个质因数,我们已经能够将n−1n-1n−1写成ax+byax+byax+by的形式。”
此时,我们会发现,出题人给我们开了一个大玩笑,我们只要输出axn+byn\color{red}只要输出\frac{ax}{n}+\frac{by}{n}只要输出nax+nby即珂。(axaxax和bybyby因为分别有a,ba,ba,b,所以显然和nnn不互质)。这个分数约分之后至少能够约成xn/a+yn/b\frac{x}{n/a}+\frac{y}{n/b}n/ax+n/by的形式,明显是满足条件的。也就是说k<=105k<=10^5k<=105是吓人的,只要k=2k=2k=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;
}
本文介绍一种算法,用于将分数1/n分解为若干个真分数之和,这些真分数的分母必须是n的因数,且分数之和等于1。算法核心在于使用n的最小质因数来构造解,确保输出的分数数量不超过10^5。
&spm=1001.2101.3001.5002&articleId=90047589&d=1&t=3&u=e212ca5ac02c4555aa71cd45893b4cab)
2万+

被折叠的 条评论
为什么被折叠?



