zoukankan      html  css  js  c++  java
  • 【题解】斐波拉契 luogu3938

    题目

    题目描述

    小 C 养了一些很可爱的兔子。 有一天,小 C 突然发现兔子们都是严格按照伟大的数学家斐波那契提出的模型来进行 繁衍:一对兔子从出生后第二个月起,每个月刚开始的时候都会产下一对小兔子。我们假定, 在整个过程中兔子不会出现任何意外。

    小 C 把兔子按出生顺序,把兔子们从 1 开始标号,并且小 C 的兔子都是 1 号兔子和 1 号兔子的后代。如果某两对兔子是同时出生的,那么小 C 会将父母标号更小的一对优先标 号。

    如果我们把这种关系用图画下来,前六个月大概就是这样的:

    其中,一个箭头 A → B 表示 A 是 B 的祖先,相同的颜色表示同一个月出生的兔子。

    为了更细致地了解兔子们是如何繁衍的,小 C 找来了一些兔子,并且向你提出了 m 个 问题:她想知道关于每两对兔子 aia_iai​ 和 bib_ibi​ ,他们的最近公共祖先是谁。你能帮帮小 C 吗?

    一对兔子的祖先是这对兔子以及他们父母(如果有的话)的祖先,而最近公共祖先是指 两对兔子所共有的祖先中,离他们的距离之和最近的一对兔子。比如,5 和 7 的最近公共祖 先是 2,1 和 2 的最近公共祖先是 1,6 和 6 的最近公共祖先是 6。

    输入输出格式

    输入格式:

    从标准输入读入数据。 输入第一行,包含一个正整数 m。 输入接下来 m 行,每行包含 2 个正整数,表示 aia_iai​ 和 bib_ibi​ 。

    输出格式:

    输出到标准输出中。 输入一共 m 行,每行一个正整数,依次表示你对问题的答案。

    输入输出样例

    输入

    5 
    1 1 
    2 3 
    5 7 
    7 13 
    4 12
    

    输出

    1 
    1 
    2 
    2 
    4
    

    说明

    题解

    分析

    首先考虑70%的数据,

    每天新出生的兔子数目一定是f[i],这个很容易计算得出

    然后发现,这f[i]只兔子的父亲一定是1~f[i],于是模拟这个过程,做一遍LCA即可

    再考虑100%的数据,

    n达到int以上,无法模拟,

    s[i]=∑f[1~i],发现第s[i]+1只兔子父亲肯定是1,第s[i]+2只兔子父亲肯定是2,第s[i]+f[i+1]只兔子父亲一定是f[i+1]

    于是有思路:二分s数组,使得s[i]+1<=a<=s[i]+f[i+1],这时候a的父亲就是a-s[i]

    这样的话一开始a是大于s[i]的,减过之后就小于s[i]了,至少折半,效率至多是O(logn),感觉挺快的

    对b也做一遍,记录他们的“祖先历程”,然后用两个指针找一下公共祖先就可以了

    实际上我是处理到10^6的,如果小于10^6就直接做倍增了,这样可能快一些,不过事实证明是差不多的

    代码

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<vector>
    #define ll long long
    #define maxn 1000005
    #define lo 21
    using namespace std;
    
    inline ll read() {
        ll x=0,w=1;
        char ch=getchar();
        while(ch!='-'&&(ch<'-'||ch>'9')) ch=getchar();
        if(ch=='-') w=-1,ch=getchar();
        while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-48,ch=getchar();
        return x*w;
    }
    
    int fa[lo][maxn];
    ll M=1e12+50;
    ll d[maxn];
    ll f[maxn],sum[maxn];
    vector<ll> fath[2];
    
    int lca(int,int);
    void work(ll,int);
    
    int main() {
        freopen("fibonacci.in","r",stdin);
        freopen("fibonacci.out","w",stdout);
        f[0]=f[1]=f[2]=1;
        sum[0]=1;sum[1]=2;sum[2]=3;
        fa[0][2]=fa[0][3]=1;
        d[2]=d[3]=1;
        register int i=3,j=0;
    
        while(1) 
        {
            f[i]=f[i-1]+f[i-2];
            sum[i]=sum[i-1]+f[i];
            if(sum[i]>M) break;
            i++;
        }
        i=3;
        while(1) 
        {
            for(j=sum[i-1]+1; j<=sum[i]&&j<maxn; j++) 
            {
                fa[0][j]=j-sum[i-1];               /*根据规律找父亲节点*/
                d[j]=d[j-sum[i-1]]+1;
            }
    
            if(j>=maxn) break;
            i++;
        }
        for(int k=1; k<lo; k++) for(int i=1; i<maxn; i++) fa[k][i]=fa[k-1][fa[k-1][i]];            
        int T=read();
    
        for(register int i=1;i<=T;++i)
        {
            ll a=read(),b=read();
            if(a==b) 
            {
                printf("%lld
    ",a);
                continue;
            }
    
            if(a<maxn&&b<maxn)    /*小于1e6的数据直接做lca*/
            {
                printf("%d
    ",lca(a,b));
            } 
            else 
            {
                fath[0].clear(),fath[1].clear();
                work(a,0);
                work(b,1);
                int i=0,j=0,flag=0;
    
                while(i<fath[0].size()&&j<fath[1].size())   /*lca*/
                {
                    if(fath[0][i]==fath[1][j]) 
                    {
                        printf("%lld
    ",fath[1][j]);
                        flag=1;
                        break;
                    }
                    if(fath[0][i]>fath[1][j]) i++;
                    else j++;
                }
    
                if(!flag) 
                {
                    a=fath[0][fath[0].size()-1],b=fath[1][fath[1].size()-1];
                    printf("%d
    ",lca(a,b));
                }
            }
        }
        return 0;
    }
    
    int lca(int x,int y)   
    {
        if(d[x]<d[y]) swap(x,y);
        for(register int k=d[x]-d[y],p=0; k; p++,k>>=1) if(k&1) x=fa[p][x];
        if(x==y) return x;
        for(register int k=lo-1; k>=0; k--) if(fa[k][x]!=fa[k][y]) x=fa[k][x],y=fa[k][y];
        return fa[0][x];
    }
    
    void work(ll a,int t)    /*对于大于1e6的数据找祖先*/
    {
        ll x=a;
        fath[t].push_back(x);
        while(1) 
        {
            if(x<maxn) break;
            int L=0,R=60;
    
            while(R-L>1) 
            {
                int mid=(L+R)/2;
                if(sum[mid]<x) L=mid;
                else R=mid;
            }
    
            if(sum[R]<x) x=x-sum[R];
            else x=x-sum[L];
            fath[t].push_back(x);
        }
    }
    
  • 相关阅读:
    table表框去掉相邻的间隔
    各种日期格式化返回
    校验金额、大小写字母、大写字母、合法uri、email
    vue js校验金钱、数字
    vue-router 动态添加 路由
    可视化-echarts流向图制作
    HTTP状态码
    二分查找
    编程语言的变量为啥不能是数字开头
    python位运算
  • 原文地址:https://www.cnblogs.com/bbqub/p/7774721.html
Copyright © 2011-2022 走看看