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;
}
posted @ 2020-10-03 09:04  With_penguin  阅读(107)  评论(0编辑  收藏  举报