• [ARC122C] Calculator


    前言

    现在是22:10,离比赛结束过去了10分钟。

    题目

    AtCoder

    题目大意:

    给一个目标正整数 (N),你初始有两个数 (x=0,y=0) ,有四种操作:

    1. (x) 替换为 (x+1)
    2. (y) 替换为 (y+1)
    3. (x) 替换为 (x+y)
    4. (y) 替换为 (x+y)

    要求在 (130) 次操作内将 (x) 变为目标数 (N)

    (1le Nle 10^{18}.)

    讲解

    先放一个大数镇楼:679891637638612257。

    我们先随便玩一玩发现,为了达到较大的目标状态,我们肯定需要反复使用操作 (3,4)

    我反复用的操作顺序是 (4,3)

    假设初始状态为 ((1,1)),那么之后的状态为:((1,2),(3,2),(3,5),(8,5),(8,13)...)

    很显然的斐波拉契数列。

    自然我们想到将 (N) 拆分为多个斐波拉契数相加,在适当的时机加上 ((1,1)),当然根据斐波拉契数位置的奇偶性不同,我们也可能要加上 ((2,1))

    也就是说我们将 (x)(y) 也拆分为多个斐波拉契数。

    比如我们看这个例子:(N=11=8+3)

    ((0,0;0,0),(0,1;0,1),(0,1;0,2),(0,3;0,2),(1,3;1,2),(1,3;2,5),(3,8;2,5).)

    分号前为 (x),之后为 (y)

    最终状态:(x=8+3=11,y=2+5=7.)

    到这里,我们的思路已经基本成型,但是这样还不够,细节上还需要优化,不然会被卡。

    比如我们并不需要加上 ((1,1)),因为 ((0,1)) 经过一轮 (4,3) 后可以变成 ((1,1))((1,0)) 经过一轮 (4,3) 后可以变成 ((2,1))。单独加 ((1,1))((2,1)) 显然没有一轮 (4,3) 操作优,因为 (4,3) 操作是共用的。

    详见代码。

    代码

    //12252024832524
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define TT template<typename T>
    using namespace std;
    
    typedef long long LL;
    const int MAXN = 100005;
    LL n,f[MAXN];
    
    LL Read()
    {
    	LL x = 0,f = 1;char c = getchar();
    	while(c > '9' || c < '0'){if(c == '-')f = -1;c = getchar();}
    	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
    	return x * f;
    }
    TT void Put1(T x)
    {
    	if(x > 9) Put1(x/10);
    	putchar(x%10^48);
    }
    TT void Put(T x,char c = -1)
    {
    	if(x < 0) putchar('-'),x = -x;
    	Put1(x); if(c >= 0) putchar(c);
    }
    TT T Max(T x,T y){return x > y ? x : y;}
    TT T Min(T x,T y){return x < y ? x : y;}
    TT T Abs(T x){return x < 0 ? -x : x;}
    
    int c[MAXN],tot,ans[MAXN],anstot;
    
    void Add(int x){ans[++anstot] = x;}
    
    int main()
    {
    //	freopen(".in","r",stdin);
    //	freopen(".out","w",stdout);
    	n = Read();
    	f[1] = 1; f[2] = 2;
    	for(int i = 3;i <= 88;++ i) f[i] = f[i-1] + f[i-2];
    	for(int i = 88;i >= 1;-- i) if(n >= f[i]) c[++tot] = i,n -= f[i];
    	for(int i = 1;i <= tot;++ i)
    	{
    		if(i == tot)
    		{
    			if(c[tot] & 1) Add(2);
    			else Add(1);
    			for(int j = 1;j <= (c[tot]+1) / 2;++ j) Add(4),Add(3);
    		}
    		else
    		{
    			if(c[i] & 1) Add(2);
    			else Add(1);
    			for(int j = 1;j <= ((c[i]+1)/2-(c[i+1]+1)/2);++ j) Add(4),Add(3);
    		}
    	}
    	Put(anstot,'
    ');
    	LL x = 0,y = 0;
    	for(int i = 1;i <= anstot;++ i) 
    	{
    		Put(ans[i],'
    ');
    		if(ans[i] == 1) x++;
    		else if(ans[i] == 2) y++;
    		else if(ans[i] == 3) x += y;
    		else y += x;
    	}
    	Put(anstot,'
    ');
    	return 0;
    }
    //679891637638612257
    

    后记

    赛时因为造了一个大于 (10^{18}) 的数,操作数达到 (132) 而怀疑人生。

    不过赛时人确实有点麻了,想什么都不清楚,迷迷糊糊地就开始敲代码,结果WA到怀疑人生。

    倦了。

  • 相关阅读:
    Linux内核之旅 链表实现
    Linux内核之旅 List_entry()
    希尔排序
    华为2013校园招聘上机笔试题 ---2 字符串处理转换
    编程求凸包点集
    练习一:SQLite基本操作
    java实现单链表反转
    android-数据存储之外部file存储(sdcard)
    android-数据存储之手机内部file存储
    android-数据存储之SharedPreferences
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/14879221.html
Copyright © 2020-2023  润新知