洛谷 P2609 [ZJOI2012]数列 解题报告

P2609 [ZJOI2012]数列

题目描述

小白和小蓝在一起上数学课,下课后老师留了一道作业,求下面这个数列的通项公式:

A(0)=0
A(1)=1
A(2i)=A(i) (对于任意 i>0)
A(2i+1)=A(i)+A(i+1) (对于任意 i>0)

小白作为一个数学爱好者,很快就计算出了这个数列的通项公式。于是,小白告诉小蓝自己已经做出来了,但为了防止小蓝抄作业,小白并不想把公式公布出来。于是小白为了向小蓝证明自己的确做出来了此题以达到其炫耀的目的,想出了一个绝妙的方法:即让小蓝说一个正整数N,小白则说出 的值,如果当N很大时小白仍能很快的说出正确答案,这就说明小白的确得到了公式。但这个方法有一个很大的漏洞:小蓝自己不会做,没法验证小白的答案是否正确。作为小蓝的好友,你能帮帮小蓝吗?

输入输出格式

输入格式:

输入文件第一行有且只有一个正整数T,表示测试数据的组数。

第2~T+1行,每行一个非负整数N。

输出格式:

输出文件共包含T行。

第i行应包含一个不含多余前缀0的数,它的值应等于 (n为输入数据中第i+1行被读入的整数)

说明

对于20%的数据,N<=10^8

对于50%的数据,N<=10^12

对于100%的数据,T<=20,N<=10^100


说实话,我觉得好迷。。

胡乱推了推没搞出来。。

发现直接暴力记搜就可以了,发现每层出现的不一样的数非常少,是常数级别的

我们可以直接高精,记搜用个map搞一搞

复杂度:\(O(Tlog_{10}N(loglogN+logN))\)

对应数据组数,比较或者运算复杂度,map的复杂度和运算次数

(我瞎扯的


Code:

#include <cstdio>
#include <cstring>
#include <map>
using namespace std;
const int N=102;
struct l_num
{
    int num[N];
    l_num()
    {
        memset(num,0,sizeof(num));
    }
    l_num(char c[])
    {
        memset(num,0,sizeof(num));
        num[0]=strlen(c);
        for(int i=1;i<=num[0];i++)
            num[i]=c[num[0]-i]-'0';
    }
    bool friend operator <(l_num n1,l_num n2)
    {
        if(n1.num[0]==n2.num[0])
        {
            int pos=n1.num[0];
            while(pos&&n1.num[pos]==n2.num[pos]) --pos;
            return n1.num[pos]<n2.num[pos];
        }
        return n1.num[0]<n2.num[0];
    }
    l_num friend operator /(l_num n,int k)
    {
        for(int i=n.num[0];i>1;i--)
        {
            if(n.num[i]&1) n.num[i-1]+=10;
            n.num[i]>>=1;
        }
        n.num[1]>>=1;
        n.num[1]+=k;
        if(!n.num[n.num[0]]) --n.num[0];
        return n;
    }
    l_num friend operator +(l_num n1,l_num n2)
    {
        l_num n3;
        n3.num[0]=max(n1.num[0],n2.num[0]);
        for(int i=1;i<=n3.num[0];i++)
        {
            n3.num[i]+=n1.num[i]+n2.num[i];
            n3.num[i+1]=n3.num[i]/10;
            n3.num[i]%=10;
        }
        if(n3.num[n3.num[0]+1]) ++n3.num[0];
        return n3;
    }
}zero,one;
map <l_num,l_num> a;
l_num dfs(l_num k)
{
    if(!k.num[0]) return zero;
    if(k.num[0]==1&&k.num[1]==1) return one;
    if(a.find(k)!=a.end()) return a[k];
    if(k.num[1]&1) a[k]=dfs(k/0)+dfs(k/1);
    else a[k]=dfs(k/0);
    return a[k];
}
int main()
{
    int t;char s[N];
    scanf("%d",&t);
    one.num[0]=one.num[1]=1;
    zero.num[0]=0;
    while(t--)
    {
        scanf("%s",s);
        l_num rk(s);
        l_num ans=dfs(rk);
        for(int i=ans.num[0];i;i--)
            printf("%d",ans.num[i]);
        printf("\n");
    }
    return 0;
}


2018.9.3

posted @ 2018-09-03 17:41  露迭月  阅读(202)  评论(0编辑  收藏  举报