[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+2+2{x-2}+2x-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 ↩︎

posted @ 2017-04-19 20:17  VictBr  阅读(359)  评论(1编辑  收藏  举报