[BZOJ4135][FJOI2015]世界树

[BZOJ4135][FJOI2015]世界树

describe

奥丁杀死的巨人伊米尔后,从伊米尔的尸体上生长出来一株巨大的梣树,它是整个宇宙的核心,被称为世界之树,这个巨木的枝干构成了整个世界,它被神秘的奥术力量所守护。
奥丁发现,世界树的每个节点至多有两棵子树,其蕴含的奥术力量是子树奥术力量的最大值+1,如果一个节点没有子树,其奥术力量为1,这些节点被称为“源”。
世界树在悠长的岁月里形成了奇妙的魔法平衡,具体来说,它的左子树与右子树的奥术力量的差的绝对值不会超过1。若一个节点只有一棵子树(不妨设为左子树),则右子树的奥术力量视为0。
现在奥丁想知道,在n个节点的世界树中,最高和最低的两个“源”(即叶子节点)的深度差最大是多少?
Input
第一行一个整数T,表示数据组数。
以下T行,每行一个整数n表示世界树的节点数。
Output
T行,每行一个整数表示任意两个“源”的奥术力量的差的最大值。
HINT
对于100%的数据,1 <= n <= 10^10000, T <= 50

solution

设 $f(n)$ 为$n$个点时的答案。显然$f(n) \leq f(n+1),n \geq 6$1

考虑求满足$f(n) = h$时最小的$n$。考虑最低点深度为$x$(从0开始),那么答案最多为$x$。

此时前$x$层都是满二叉树,元素个数有$2^x-1$,其他层的元素个数设为$g(x)$,则有$g(x) = g(x-1)+g(x-1)+2^{x-1}+g(x-2)+g(x-3)+ \dots + g(0) = 3g(x-1)-g(x-2)+2^{x-2}$

设$h(x)$为答案为$x$时最小需要多少个点,则
$$
h(x) = g(x)+2^x-1 = 3h(x-1)-h(x-2)-3*2^{x-1}+2^{x-2}+2+2^{x-2}+2^x-1
$$
$$
h(x) = 3h(x-1)-h(x-2)+1
$$
$$
h(0) = 1,h(1) = 4
$$

为了方便处理,我们有:
$$
h(x) +1= 3h(x-1)-h(x-2)+1+3-1-1
$$
即:
$$
(h(x) +1)= 3*(h(x-1)+1)-(h(x-2)+1)
$$

于是只要求最大的$x$使得$h(x) \leq n$即可。预处理$h(2^x)$然后对答案二进制拆分即可。

复杂度$O(Tlog_2^2 nlog_2{log_2 n})$。需要压位高精度(应该可以法法塔)。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 3305,mod = 100000000;
char s[20100];
struct Lint{
    int len,a[N];
    bool flag;
    Lint(){}
    Lint(int p){
        len = 0;
        if (p >= 0) {a[0] = p;flag = 0;}
        else{a[0] = -p;flag = 1;}
    }
    void write(){
        for (int i = len;i >= 0;i--)
            printf("%.8d",a[i]);
        printf("\n");
    }
}n;
void read(){
    gets(s);
    int len = strlen(s);
    n.len = -1;
    for (int i = len-1;i >= 0;i-=8){
        int u = 0,v = 1;
        for (int j = 0;j < 8 && i-j >= 0;j++){
            u += v*(s[i-j]-'0');
            v = v * 10;
        }
        n.a[++n.len] = u;
    }
    if (n.len < 0) {
        n.len = 0;
        n.a[0] = 0;
    }
}
struct Matrix{
    Lint a[2][2];
}pw[20],ans,I;
Lint operator * (const Lint &A,const Lint &B){
    ll a[N];Lint C;
    C.len = A.len+B.len;
    if (A.len == 1 && A.a[0] == 0) return Lint(0);
    if (B.len == 1 && B.a[0] == 0) return Lint(0);
    for (int i = 0;i <= C.len+1;i++) a[i] = 0;
    for (int i = 0;i <= A.len;i++)
        for (int j = 0;j <= B.len;j++)
            a[i+j] += (ll)A.a[i]*B.a[j];
    for (int i = 0;i <= C.len;i++){
        a[i+1] += a[i]/mod;
        a[i] %= mod;
    }
    if (a[C.len+1] > 0) C.len++;
    for (int i = 0;i <= C.len;i++) C.a[i] = a[i];
    C.flag = A.flag ^ B.flag;
    return C;
}
bool operator <= (const Lint &A,const Lint &B){
    if (A.len < B.len) return 1;
    if (A.len > B.len) return 0;
    for (int i = A.len;i >= 0;i--){
        if (A.a[i] > B.a[i]) return 0;
        if (A.a[i] < B.a[i]) return 1;
    }
    return 1;
}
Lint add(Lint A,Lint B){
    Lint C;
    C.len = A.len+B.len+1;
    for (int i = 0;i <= C.len;i++) C.a[i] = 0;
    if (A.flag == B.flag){
        C.flag = A.flag;
        for (int i = 0;i <= A.len;i++) 
            C.a[i] = A.a[i];
        for (int i = 0;i <= B.len;i++)
            C.a[i] += B.a[i];
        for (int i = 0;i <= C.len;i++)
            if (C.a[i] >= mod){
                C.a[i] -= mod;
                C.a[i+1]++;
            }
        while (C.len && C.a[C.len] == 0) C.len--;
    }
    else{
        if (A <= B) swap(A,B);
        C.flag = A.flag;
        for (int i = 0;i <= A.len;i++) 
            C.a[i] = A.a[i];
        for (int i = 0;i <= B.len;i++) 
            C.a[i] -= B.a[i];
        for (int i = 0;i <= A.len;i++)
            if (C.a[i] < 0){
                C.a[i] += mod;
                C.a[i+1]--;
            }
        C.len = A.len;
        while (C.len && C.a[C.len] == 0) C.len--;
    }
    return C;
}
Matrix operator * (const Matrix &A,const Matrix &B){
    Matrix C;
    C.a[0][0] = C.a[0][1] = C.a[1][0] = C.a[1][1] = Lint(0);
    for (int i = 0;i < 2;i++) 
        for (int j = 0;j < 2;j++)
            for (int k = 0;k < 2;k++)
                C.a[i][j] = add(C.a[i][j],A.a[i][k]*B.a[k][j]);
    return C;
}

int T;
int main(){
    scanf("%d",&T);gets(s);
    I.a[0][0] = Lint(2);I.a[0][1] = Lint(5);
    pw[0].a[0][0] = Lint(0);pw[0].a[0][1] = Lint(-1);
    pw[0].a[1][0] = Lint(1);pw[0].a[1][1] = Lint(3);
    for (int i = 1;i <= 15;i++)
        pw[i] = pw[i-1]*pw[i-1];
    while (T--){
        read();
        if (n.len == 0 && n.a[0] == 6){
            puts("0");
            continue;
        }
        if (n.len == 0 && n.a[0] <= 3){
            puts("0");
            continue;
        }
        n = add(n,Lint(1));
        
        ans = I;int cnt = 0;
        for (int i = 15;i >= 0;i--){
            Matrix u = ans * pw[i];
            if (u.a[0][0] <= n){
                ans = u;
                cnt += 1<<i;
            }
        }
        printf("%d\n",cnt);
    }
    return 0;
}

  1. 可以证明当n一定大时这个式子是成立的,反例有n=6

转载于:https://www.cnblogs.com/victbr/p/6735439.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值