(Luogu Solution)P3424:[POI2005]SUM-Fibonacci Sums

本题解搬自 zimpha’s blog:Zeckendorf Arithmetic

  • 【题目链接】

Link:P3424

  • 【解题方法】

题目中的整数表示方式其实就是Zeckendorf representation

这一题实际上就是做加法。

首先,我们像高精加一样,直接把两个数的Zeckendorf representation加起来。就是,一位一位的加起来。拿样例举例,加完了之后像这样:1 1 0 2 0(题目中输入是从低位到高位,这里采用从高位到低位)。

然后,我们考虑把那些不合法的地方消去。具体来讲,从高到低遍历。我们的首要任务是把 2 2 2 3 3 3 消去。当然,本来是没有 3 3 3 的。不过,我们在把 2 2 2 消去的过程中,可能会把某个 2 2 2 1 1 1。如果我们用 x \color{brown}x x 代表 x + 1 x+1 x+1,规则是这样的:

020 x → 100 x 030 x → 110 x 021 x → 110 x 012 x → 101 x \begin{aligned} 020x&\to100{\color{brown}x}\\030x&\to110{\color{brown}x}\\021x&\to110 x\\012x&\to 101x \end{aligned} 020x030x021x012x100x110x110x101x

当然,我们可能要特殊处理最后几位。不过,如果我们把 F 0 = 1 F_0=1 F0=1 F − 1 = 0 F_{-1}=0 F1=0 加入表示中,我们就可以不用特殊处理。不过,如果表示中含有 F − 1 F_{-1} F1,那直接忽略就行了。如果含有 F 0 F_{0} F0 呢?我们稍后处理。

现在我们处理连续的 1 1 1。我们用两次 011 → 100 011\to100 011100,第一次从高位到低位,第二次从低位到高位。第一次做完后,没有连续的 3 3 3 1 1 1,表示中不可能同时含有 F 0 F_0 F0 F 1 F_1 F1,所以如果表示中有 F 0 F_0 F0 那么直接把它变成 F 1 F_1 F1 即可。在这个基础上,第二遍直接做即可。

  • 【代码实现】
#include <iostream>
#include <array>

using namespace std;

array<int,1000002> fib_rep;

int main(int argc,char* argv[],char* envp[])
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr);
	int cnt;
	cin>>cnt;
	for(int i=2;i<cnt+2;i++)
		cin>>fib_rep[i];
	cin>>cnt;
	for(int i=2;i<cnt+2;i++)
		cin>>fib_rep[0],fib_rep[i]+=fib_rep[0];
	fib_rep[0]=0;
	for(int i=1000001;i>=2;i--)
		if(fib_rep[i]==0&&fib_rep[i-1]==2&&fib_rep[i-2]==0)
			fib_rep[i]=1,fib_rep[i-1]=0,fib_rep[i-2]=0,fib_rep[i-3]++;
		else if(fib_rep[i]==0&&fib_rep[i-1]==3&&fib_rep[i-2]==0)
			fib_rep[i]=1,fib_rep[i-1]=1,fib_rep[i-2]=0,fib_rep[i-3]++;
		else if(fib_rep[i]==0&&fib_rep[i-1]==2&&fib_rep[i-2]==1)
			fib_rep[i]=1,fib_rep[i-1]=1,fib_rep[i-2]=0;
		else if(fib_rep[i]==0&&fib_rep[i-1]==1&&fib_rep[i-2]==2)
			fib_rep[i]=1,fib_rep[i-1]=0,fib_rep[i-2]=1;
	for(int i=1000001;i>=2;i--)
		if(fib_rep[i]==0&&fib_rep[i-1]==1&&fib_rep[i-2]==1)
			fib_rep[i]=1,fib_rep[i-1]=0,fib_rep[i-2]=0;
	if(fib_rep[1]==1)
		fib_rep[2]=1;
	for(int i=4;i<=1000001;i++)
		if(fib_rep[i]==0&&fib_rep[i-1]==1&&fib_rep[i-2]==1)
			fib_rep[i]=1,fib_rep[i-1]=0,fib_rep[i-2]=0;
	int ptr;
	for(ptr=1000001;!fib_rep[ptr];ptr--);
	cout<<ptr-1<<' ';
	for(int i=2;i<=ptr;i++)
		cout<<fib_rep[i]<<' ';
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值