ARC122CCalculator

Posted stoorz

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ARC122CCalculator相关的知识,希望对你有一定的参考价值。

题目

题目链接:https://atcoder.jp/contests/arc122/tasks/arc122_c
有两个变量 \\(x,y\\),初始时均为 \\(0\\)
可以执行以下操作不超过 \\(130\\) 次:

  1. \\(x\\) 赋值为 \\(x+1\\)
  2. \\(y\\) 赋值为 \\(y+1\\)
  3. \\(x\\) 赋值为 \\(x+y\\)
  4. \\(y\\) 赋值为 \\(x+y\\)

给定一个数字 \\(n\\),要求给出一个操作序列使得依次操作后 \\(x\\) 的值等于 \\(n\\)
\\(n\\leq 10^{18}\\)

思路

我们发现操作 \\(3,4\\) 其实就是在计算斐波那契数列。而众所周知任意一个正整数 \\(n\\) 都可以被拆分为若干个不同的斐波那契数的和,所以考虑用斐波那契数的和凑出 \\(n\\)
如果 \\(n\\) 就是斐波那契数列中的一项,那么只需要先进行一次操作 \\(1\\),然后不断进行操作 \\(3,4,3,4\\) 即可。
如果 \\(n\\) 等于斐波那契数列的第 \\(a_1,a_2\\cdots a_k(a_1<a_2<\\cdots<a_k)\\) 项之和,那么我们先按照上述方法构造出一个序列使得 \\(x=a_k\\),然后在倒数第 \\(a_i(1\\leq i<k)\\) 次操作后面加入一个 \\(1\\)\\(2\\)(往 \\(x\\)\\(y\\) 中大的那一个上面加),那么最终的结果就是 \\(a_1+a_2+\\cdots+a_k=n\\) 了。
例如 \\(17=1+3+13\\),构造方法如下:

\\[(0,0)\\to (1,0)\\to (1,1)\\to (2,1)\\to (2,3)\\to (2,3+1)\\to (5+1,3+1)\\to (5+1,8+2)\\to (13+3,8+2)\\to (13+3+1,8+2) \\]

注意有可能操作后是 \\(y\\)\\(n\\),此时把操作取反一下即可。
操作次数的话,斐波那契数列第 \\(88\\) 项超过了 \\(10^{18}\\),所以操作 \\(3,4\\) 的次数最多是 \\(87\\),操作 \\(1,2\\) 的次数为 \\(n\\) 拆分成的斐波那契数的数量,由于不可能拆分成相邻的两个斐波那契数(他们的和等于下一个斐波那契数),且斐波那契数列第一项和第二项都是 \\(1\\),所以至多拆分为 \\(\\text{fib}_2+\\text{fib}_4+\\cdots+\\text{fib}_{86}\\),一共 \\(43\\) 项。所以操作次数上界为 \\(87+43=130\\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=100;
const ll Inf=1e18;
ll n,m,p,fib[N];
queue<int> q;
vector<int> a;

int main()
{
	scanf("%lld",&n);
	fib[1]=1; p=n;
	for (m=2;fib[m-1]<=n;m++)
		fib[m]=fib[m-1]+fib[m-2];
	for (int i=m;i>=1;i--)
		if (p>=fib[i]) p-=fib[i],a.push_back(i);
	a.push_back(2);
	int v=(!(a[0]&1))+3;
	for (int i=1;i<a.size()-1;i++)
	{
		q.push(5-v);
		for (int j=a[i];j>a[i+1];j--)
			q.push(v),v=7-v;
	}
	q.push(3);
	cout<<q.size()<<"\\n";
	for (;q.size();q.pop())
		cout<<q.front()<<"\\n";
	return 0;
}

以上是关于ARC122CCalculator的主要内容,如果未能解决你的问题,请参考以下文章

ARC122A Many Formulae 题解

[ARC122E]Increasing LCMs

[ARC 122]

[ARC122C] Calculator

[ARC122D]XOR Game

[ARC122D]XOR Game