Codeforce 1089F Fractions 题解(数论,思维,卡常)

题意简述

给定一个 n n n,确定一些分数,使得这些分数的分母是 n n n的因数,且 &lt; n &lt;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 &lt; = 1 0 5 &lt;=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} nn1,由于要求分母都是 b b b的因数,我们不妨将这些分数都通分,就又变成:将 n − 1 n-1 n1分成一些不和 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,ya也是一个满足条件的解(代入一下会发现两个 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 &lt; a y&lt;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 n1写成 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 &lt; = 1 0 5 k&lt;=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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值