[COCI2020/2021]Euklid

壹、题目描述 ¶

定义函数

\[R(a,b)= \begin{cases} R(b,a) &a<b \\ R(\lfloor{a\over b}\rfloor,b)&a\ge b>1 \\ a&b=1 \end{cases} \]

给定 \(g,h\),请找到 \(a,b\) 使得 \(\gcd(a,b)=g\and R(a,b)=h\). 保证 \(g,h\le 200000\),可以证明,存在 \(a,b\le 10^{18}\) 的解。

贰、题解 ¶

tl;dr —— 输出:

\[b=g\left\lceil{h^K\over g}\right\rceil,a=hb+g\quad(g<h^K) \]

你可以获得 \(100pts\).

显然一道构造题,但是挺难想到。下面我们不妨假设 \(a\ge b\).

首先想到的就是去考察 \(R\) 的性质,如果你打表,你会发现......不,你什么都发现不了。

但是我们可以利用人类智慧,首先一定有 \(R(h,h^k)=h\),更一般地,有 \(R(h,h^k+r)=h\),我们只需要保证 \(r<h^k\) 即可。

也即,有 \(R(h,x)=h\Big(x\in [h^k,2h^k)\Big)\),如果我们不考虑 \(\gcd\) 的限制,那么我们可以直接构造 \(a=h^k,b=h\),但是像这样由一边除到尽头,我们始终需要强制让一边为 \(h\),如 \(b=h\),而此时我们是万般不能让 \(\gcd(a,b)=g\) 的(除了可能存在的一些特殊情况)。所以,这种 “一边除到底” 好是好,但是不能满足所有条件。

但是我们可以考虑,当 \(a/b\) 完之后,得到 \(a'=h\),然后我们面对的是 \(R(a',b)=R(h,b)\),然后用 \(h\)\(b\) 除成 \(1\),这个时候,\(a=h\times b^j+r(r<b^j)\),且 \(b\in[h^k,2h^k)\),并且得构造出 \(\gcd(a,b)=g\).

根据辗转相除法,我们有

\[\gcd(a,b)=g \\ \Rightarrow gcd(h\times b^j+r,b)=\gcd(r,b)=g \]

如果设 \(b=gh\),考虑最简单的情况,\(r=g\and j=1\Rightarrow a=h^2g+g=g(h^2+1)\),我们一定能够保证 \(\gcd(h,h^2+1)=1\),这样似乎合法?

然后,你会发现你错了,举个栗子:当 \(g=2,h=3\) 时,我们可以构造出 \(a=20,b=6\),但是 \(R(20,6)=2\),那么问题出在哪里?我们还有个前提条件是 \(gh\in [h^k,2h^k)\Rightarrow g\in[h^{k-1},2h^{k-1})\),得保证这个,我们才能构造出正确的答案。

也就是说,如果 \(g\) 给得不够好,就没有答案咯?那为什么题目 “可以证明” 存在 \(a,b\le 10^{18}\)?事实上,\(b=gh\) 只是我猜测的其中一种情况,\(b\) 不一定一定就是 \(gh\),我们可以记 \(b=gi\),保证 \(b=gi\in [h^k,2h^k)\) 即可,即,\(g\in\left[{h^k\over i},{2h^k\over i}\right)\) 即可。

至于如何找到这个 \(i\),由于我们有

\[h^k\le gi<2h^k \\ \Rightarrow {h^k\over g}\le i<{2h^k\over g} \]

因为这个 \(i\) 是个整数,我们只需要找到一个 \(k\),满足区间 \(\left[{h^k\over g},{2h^k\over g}\right)\) 包含至少一个整数即可,一个一定满足条件的不等式是

\[{h^k\over g}+1<{2h^k\over g} \\ \Rightarrow g<h^k \]

只需要找到这个 \(k\) 就可以了,取 \(i=\left\lceil{h^k\over g}\right\rceil\),而 \(a=h\times b+g\).

但是值得注意的是,直接取 \(i=\left\lceil{h^k\over g}\right\rceil\) 的同时,我们一定有 \(i>1\),因为 \(g<h^k\),但是至于为什么必须保证 \(i>1\) 呢?因为我们的 \(a=h\times b^j+r(r<b^j)\),在此我们取 \(j=1\),则需保证 \(r=g<b=gi\),显然,当 \(i=1\) 时不等式不成立,但是我们取上取整保证了 \(i>1\).

总复杂度 \(\mathcal O(T\log_hg)\).

题解过于简单,直接给出构造方法:

\[b=g\left\lceil{h^K\over g}\right\rceil,a=hb+g\quad(g<h^K) \]

我只能说,这个构造方法没有时间还真想不到,或许只有 \(\sf SYDevil\)\(\sf OID\) 可以直接出吧?

另:可能会有其他构造方法,不过,愚以为,所以不同的构造方法实际上都是 \(r,i,j\) 取不同的值(此处取值 \(r=g,j=1\)),只要满足 \(\gcd(r,b)=g\) 以及 \(r<b^j\),然后得到不同的约束条件,本质应该都是这种构造方法(除非你交换这两个数不止一,但是按道理应该也可以归为这种只交换一次位置)。如果有其他的构造方法,且不满足作者的待定系数的,请联系作者,十分感谢!

叁、参考代码 ¶

#include<cstdio>
#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;

// #define NDEBUG
#include<cassert>

namespace Elaina{
    #define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
    #define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
    #define fi first
    #define se second
    #define mp(a, b) make_pair(a, b)
    #define Endl putchar('\n')
    #define mmset(a, b) memset(a, b, sizeof a)
    // #define int long long
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair<int, int> pii;
    typedef pair<ll, ll> pll;
    template<class T>inline T fab(T x){ return x<0? -x: x; }
    template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
    template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
    template<class T>inline T readin(T x){
        x=0; int f=0; char c;
        while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
        for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
        return f? -x: x;
    }
    template<class T>inline void writc(T x, char s='\n'){
        static int fwri_sta[1005], fwri_ed=0;
        if(x<0) putchar('-'), x=-x;
        do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
        while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
        putchar(s);
    }
}
using namespace Elaina;

ull R(ull a, ull b){
    if(a<b) return R(b, a);
    if(b==1) return a;
    return R(a/b, b);
}
ull gcd(ull a, ull b){ return b? gcd(b, a%b): a; }

ull g, h, k, tmp, i, a, b;

signed main(){
    freopen("euklid.in", "r", stdin);
    freopen("euklid.out", "w", stdout);
    rep(_, 1, readin(1)){
        g=readin(1llu), h=readin(1llu);
        tmp=1, k=0;
        while(g>=tmp) ++k, tmp*=h;
        // When x,y are integers the equation ceil(x/y)=(x+y-1)/y is established
        i=(tmp+g-1)/g, b=g*i, a=h*b+g;
        writc(a, ' '), writc(b);
    }
    return 0;
}

肆、关键的地方 ¶

还是挖掘这个性质最重要吧,即 \(R(x,y)=x\Big(y\in [x^k,2x^k)\Big)\).

另外,考试时放弃这个方法的原因在于,我没有认真思考 “一边除到底” 的方法是可以扩展的,即可以将一边除成 \(h\),然后再把另一边调整为 \(1\),也就是我没有想到后面还可以交换一个位置来处理。

posted @ 2021-06-19 16:38  Arextre  阅读(113)  评论(0编辑  收藏  举报