Fraction Construction Problem 2020牛客多校第三场(扩展欧几里得)

原题题面

有T个输入。
每组输入给出两个正整数 a , b ( a , b ≤ 2 × 1 0 6 ) a,b(a,b\leq 2 × 10^6) a,b(a,b2×106)
请找到四个正整数 c , d , e , f ( f < b , d < b , 1 ≤ c , e ≤ 4 × 1 0 12 ) c,d,e,f(fc,d,e,f(f<b,d<b,1c,e4×1012),使他们满足:
c d − e f = a b \frac{c}{d}-\frac{e}{f}=\frac{a}{b} dcfe=ba
如果有多组数据,输出其中一组。如果不存在,输出“-1 -1 -1 -1”。

输入样例

3
4 1
1 6
37 111

输出样例

-1 -1 -1 -1
1 2 1 3
145 87 104 78

题面分析

首先显然,当 g c d ( a , b ) gcd(a,b) gcd(a,b)不为1时,我们可以令 a ′ = a g c d ( a , b ) , b ′ = b g c d ( a , b ) a'=\frac{a}{gcd(a,b)},b'=\frac{b}{gcd(a,b)} a=gcd(a,b)a,b=gcd(a,b)b。构造
a ′ + 1 b ′ − 1 b ′ = a b \frac{a'+1}{b'}-\frac{1}{b'}=\frac{a}{b} ba+1b1=ba
所以输出"a’+1 b’ 1 b’"即可。
g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1时,易知
c d − e f = c f − d e d f \frac{c}{d}-\frac{e}{f}=\frac{cf-de}{df} dcfe=dfcfde
d f = k b df=kb df=kb(k为正整数)
首先我们可以证明一下结论:
结论①: b b b不能是质数或1。
证明:
b不能是1显然。
假设 b b b是质数,那 b b b的因子只有 1 , b 1,b 1,b,即 d , f d,f d,f只能分别取1和b的倍数,这与 d < b , f < b dd<b,f<b矛盾。故①得证。
结论②: b b b不能是质数幂(即 p k p^k pk)
证明:
假设 b = p k b=p^k b=pk,设 d = p a d=p^a d=pa,则 f = p k − a f=p^{k-a} f=pka,由扩展欧几里得得知 c f − d e = a cf-de=a cfde=a有正整数解的充要条件是 g c d ( d , f ) ∣ a gcd(d,f)|a gcd(d,f)a g c d ( d , f ) = p m i n { a , k − a } gcd(d,f)=p^{min\{a,k-a\}} gcd(d,f)=pmin{a,ka}。但因为 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1,故 a a a的质因子里没有 p p p,即 g c d ( d , f ) ∤ a gcd(d,f)\nmid a gcd(d,f)a,矛盾。故②得证。
由结论①②可知结论③: b b b的质因子至少拥有两个
根据③,我们设 b = ∑ i = 1 m p i k i b=\sum_{i=1}^{m}p_{i}^{k_i} b=i=1mpiki,令 d = p 1 k 1 , f = b / d , d=p_{1}^{k_1},f=b/d, d=p1k1,f=b/d,易知 g c d ( d , f ) = 1 gcd(d,f)=1 gcd(d,f)=1
c f − d e = a cf-de=a cfde=a必有解,可以用扩展欧几里得求出 c , e c,e c,e
需要注意的是,如果最后 c < 0 , e > 0 c<0,e>0 c<0,e>0,其实不需要对扩展欧几里得的解做一些操作,只需要调换 c / d , e / f c/d,e/f c/d,e/f的位置即可。

AC代码(123ms)

#include 
using namespace std;
const int MAXN=5e6;
int prime[1000];//素数数组
bool is_prime[MAXN+10];//is_pri[i]表示i是否是素数
int sieve()//埃式筛
{
    int n=MAXN;
    int p=0;
    for(int i=0; i<=n; i++)
        is_prime[i]=true;
    is_prime[0]=is_prime[1]=false;
    is_prime[2]=true;

    for(int i=2; i<=sqrt(n); i++)
    {
        if (is_prime[i])
        {
            prime[++p]=i;
            for(int j=2*i; j<=n; j+=i)
                is_prime[j]=false;
        }
    }
    return p;
}
long long extended_gcd(long long a, long long b, long long &x, long long &y)
//扩展gcd
{
    long long r, t;
    if (b==0)
    {
        x=1;
        y=0;
        return a;
    }
    r=extended_gcd(b, a%b, x, y);
    t=x;
    x=y;
    y=t-(a/b)*y;
    return r;
}
long long solve_exgcd(long long a, long long b, long long c, long long &x, long long &y)
{
    long long p=extended_gcd(a, b, x, y);
    if (c%p)
    {
        x=-1;
        y=-1;
        return -1;
    }
    x*=(c/p);
    y*=(c/p);
    return 0;
}
void solve1()
{
    long long pp=sieve();
    int t;
    scanf("%d", &t);
    while(t--)
    {
        long long a, b;
        scanf("%lld%lld", &a, &b);
        if (__gcd(a, b)!=1)// ka/kb
        {
            long long v=__gcd(a, b);
            a/=v;
            b/=v;
            printf("%lld %lld %lld %lld\n", a+1, b, 1, b);
            continue;
        }
        else
        {
            if (b==1 || is_prime[b])//特判无解情况1
            {
                printf("-1 -1 -1 -1\n");
                continue;
            }
            long long sum=0;
            long long tmp,time=0;
            long long bb=b;
            for(int i=1;i<=pp && bb!=1;i++)
            {
                if(bb%prime[i]==0)
                {
                    tmp=prime[i];
                    while(bb%prime[i]==0)
                    {
                        bb/=prime[i];
                        time++;
                    }
                    break;
                }
            }
            if(bb==1)//特判情况2
            {
                printf("-1 -1 -1 -1\n");
                continue;
            }
            long long d=1;
            for(int i=1; i<=time; i++)
            {
                d*=tmp;
            }
//            printf("d=%lld\n",d);
            long long f=bb;
            long long c=0, e=0;
            solve_exgcd(f, d, a, c, e);
            if (c<0 && e>0)
                printf("%lld %lld %lld %lld\n", e, f, -c, d);//两个调过来
            else
                printf("%lld %lld %lld %lld\n", c, d, -e, f);
        }
    }
}
int main()
{
//    ios_base::sync_with_stdio(false);
//    cin.tie(0);
//    cout.tie(0);
#ifdef ACM_LOCAL
    freopen("in.txt", "r", stdin);
    freopen("out.txt", "w", stdout);
    long long test_index_for_debug=1;
    char acm_local_for_debug;
    while(cin>>acm_local_for_debug)
    {
        cin.putback(acm_local_for_debug);
        if (test_index_for_debug>100)
        {
            throw runtime_error("Check the stdin!!!");
        }
        auto start_clock_for_debug=clock();
        solve1();
        auto end_clock_for_debug=clock();
        cout<<"\nTest "<<test_index_for_debug<<" successful"<<endl;
        cerr<<"Test "<<test_index_for_debug++<<" Run Time: "
            <<double(end_clock_for_debug-start_clock_for_debug)/CLOCKS_PER_SEC<<"s"<<endl;
        cout<<"--------------------------------------------------"<<endl;
    }
#else
    solve1();
#endif
    return 0;
}

后记

赛时差一点就对了…
感谢赛后蔡队的指点 (CSLNB!)
DrGilbert 2020.7.18

你可能感兴趣的:(数论)