P4583 [FJOI2015] 世界树

题意

要求构建一个 \(n\) 个节点的二叉树,其中叶子结点被称为“源”,要求每个节点的左右子树中最深的叶子节点深度相差不超过 \(1\),问两个叶子结点深度差的最大值,\(T\) 组询问。
\(n\le10^{10000},T\le50\)

思路

令根节点深度为 \(1\)
\(f_i\) 表示最深的“源”深度为 \(i\) 的“源”最少所需的节点数。\(f_i=f_{i-1}+f_{i-2}+1\),即新增一个根结点,左子树方深度为 \(i-1\) 的“源”,右子树放深度为 \(i-2\) 的“源”,此时最浅“源”的深度为 \(\lceil\frac{i+1}2\rceil\)
对于 \(n\),找到最大的 \(i\) 满足 \(f_i\le n\),答案即位 \(i-\lceil\frac{i+1}2\rceil\)
但是这样只能拿 \(90\) 分,因为当 \(n=6\) 时,这个算法会算出 \(1\),但是答案为 \(0\)。特判掉就能 \(AC\) 了。

代码

由于这个题目非常恶心,需要把询问离线下来,用滚动的方式递推,不然会 \(MLE\),还需要压位高精度,不然会 \(TLE\)

/*
Luogu P4583 [FJOI2015] 世界树
2026-04-14
*/
#include<bits/stdc++.h>
using namespace std;
namespace IO{
    template<typename T>
    inline void read(T&x){
        x=0;char c=getchar();bool f=0;
        while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
        while(isdigit(c)) x=x*10+c-'0',c=getchar();
        f?x=-x:0;
    }
    template<typename T>
    inline void write(T x){
        if(x==0){putchar('0');return ;}
        x<0?x=-x,putchar('-'):0;short st[50],top=0;
        while(x) st[++top]=x%10,x/=10;
        while(top) putchar(st[top--]+'0');
    }
    inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
    inline void write(char c){putchar(c);}
    inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
    inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
    template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
    template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
    template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
struct Int{
    vector<int>a;
    friend void write(Int x){
        if(x.a.size()==0){write(0);return ;}
        for(int i=x.a.size()-1;i>=0;i--) write(x.a[i]);
    }
    friend void read(Int&x){
        x.a.clear();
        string s;read(s);
        int be=0;
        while(be<s.size()&&s[be]=='0') be++;
        if(be==s.size()){x.a.push_back(0);return;}
        s=s.substr(be);
        int len=s.size();
        for(int i=len;i>0;i-=8){
            int ls=0;
            for(int j=max(0,i-8);j<i;j++) ls=ls*10+(s[j]-'0');
            x.a.push_back(ls);
        }
    }
    Int(int x){while(x) a.push_back(x%100000000),x/=100000000;}
    Int(){a.clear();}
    void clear(){a.clear();}
    Int operator+(const Int&x)const{
        vector<int>b=x.a,a=this->a;
        int n=max(b.size(),a.size());
        b.resize(n+1),a.resize(n+1);
        for(int i=0;i<n;i++) a[i]+=b[i];
        for(int i=0;i<n;i++) a[i+1]+=a[i]/100000000,a[i]%=100000000;
        while(!a.empty()&&!a.back()) a.pop_back();
        Int A;
        A.a.swap(a);
        return{A};
    }
    bool operator<=(const Int&x)const{
        if(a.size()<x.a.size()) return 1;
        if(a.size()>x.a.size()) return 0;
        for(int i=a.size()-1;i>=0;i--) if(a[i]<x.a[i]) return 1;else if(a[i]>x.a[i]) return 0;
        return 1;
    }
};
Int n,a,b;
int cnt,ans[100];
struct que{
    Int n;int id;
    bool operator<(const que&t){return n<=t.n;} 
}q[100];
void solve(){
    read(n);
    cnt++;
    q[cnt]={n,cnt};
}
signed main(){
    int T;read(T);
    for(int i=1;i<=T;i++) solve();
    sort(q+1,q+1+T);
    a=1,b=2;
    int w=1;
    for(int i=1;;i++){
        while(q[w].n<=b){
            ans[q[w].id]=i-ceil((i+1)/2.0);
            w++;
            if(w>T) break;
        }
        if(w>T) break;
        a=a+b;
        swap(a,b);
        b=b+1;
    }
    for(int i=1;i<=T;i++) if(q[i].n<=6&&(Int)6<=q[i].n) ans[q[i].id]=0;
    for(int i=1;i<=T;i++) write(ans[i]),write("\n");
    return 0;
}
posted @ 2026-04-16 21:49  Link-Cut_Trees  阅读(4)  评论(0)    收藏  举报