UOJ #216. 【UNR #1】Jakarta Skyscrapers
先考虑有一个 doge 在 \(1\) 处的情况。假设另一个 doge 在 \(n\) (\(n\) 足够大),那么我们可以依次获得 \(n - 1, n - 2, 2, n - 4, 4, n - 8\cdots\)。发现这样我们可以通过二进制分解在限定次数内获得任意一个数。
再考虑一般情况,给定 \(a,b,c\) 若 \((a,b)\) 不整除 \(c\) 则显然无解。 定义新的 \(a,b,c\) 为原来的除以 \((a,b)\)(可以在最后乘回来)。现在相当于我们要造出来一个 \(1\)。考虑辗转相除法,如何快速对一个数取模?直接使用前面的倍增法就可以了。
代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<set>
#include<cmath>
#define S_IT set <LL> ::iterator
using namespace std;
typedef long long LL;
const int N = 1000;
LL a, b, c, d, ansX[N], ansY[N], cnt;
set <LL> S;
LL push(LL x, LL y)
{
if (x > y && S.find(x - y) == S.end() && y > 0)
S.insert(x - y), ansX[++cnt] = x, ansY[cnt] = y;
return x - y;
}
void init()
{
scanf("%lld %lld %lld", &a, &b, &c);
if (a < b) swap(a, b);
d = __gcd(a, b);
}
LL solve(LL a, LL b, LL c) //return a - b * c
{
LL k = ceil(log(c) / log(2)), x = push(a, b), y;
for (int i = 0; i <= k; i++)
y = push(a, x), x = push(x, y);
for (int i = k; i >= 0; i--)
if (c & (1ll << i))
a = push(a, b << i);
return a;
}
void print()
{
printf("%lld\n", cnt);
for (int i = 1; i <= cnt; i++)
printf("%lld %lld\n", ansX[i] * d, ansY[i] * d);
}
void work()
{
if (c > a || c % d != 0)
{
puts("-1");
return;
}
a /= d, b /= d, c /= d;
S.insert(a), S.insert(b);
for (LL t; b != 1; a = b, b = t)
t = solve(a, b, a / b);
S_IT it = S.lower_bound(c);
if (c == *it)
print();
else
solve(*it, 1, *it - c), print();
}
int main()
{
init();
work();
return 0;
}
由于博主比较菜,所以有很多东西待学习,大部分文章会持续更新,另外如果有出错或者不周之处,欢迎大家在评论中指出!