[LuoguP4808][CCC 2018]平衡树(数论分块+记忆化搜索)(有复杂度证明)

[LuoguP4808][CCC 2018]平衡树(数论分块+记忆化搜索)(有复杂度证明)

题面

我们定义「完美平衡树」如下:
每棵完美平衡树都有一个正整数权值。权值为 \(1\) 的完美平衡树为只含有 \(1\) 个节点的树。否则,这棵树的权值为 \(w(w\ge2)\),则这棵树为一棵含有 \(k(2\le k\le w)\) 棵子树的有根树。所有的 \(k\) 棵子树都必须是相同的,且它的所有 \(k\) 棵子树必须完全相同,且自身是完美平衡的。

特别地,所有 \(k\) 棵子树权值必须相同。它们的权值必须为 \(\left\lfloor\frac{w}{k}\right\rfloor\) 。例如,如果一棵权值为 \(8\) 的完美平衡树有 \(3\) 棵子树,那么每棵子树的权值为 \(2\),因为 \(2+2+2=6\le8\)

给定 \(N(1\leq n \leq 10^9)\),求出权值为 \(N\) 的完美平衡树的数量。

分析

简化版题面.已知\(f_1=1,f_n=\sum_{i=2}^n f_{\lfloor\frac{n}{i} \rfloor}(n \geq 2)\),求\(f_n(n \leq 10^9)\)

首先,可以利用数论分块把\(f_{\lfloor\frac{n}{i} \rfloor}\)的枚举优化到\(O(\sqrt n)\),然后注意到\(f_n\)用到的状态不多,可以用记忆化搜索,于是就跑过了。

时间复杂度\(O(n^{\frac{3}{4}})\),证明如下:
由于\(\lfloor\frac{n}{xy}\rfloor=\lfloor \frac{\lfloor\frac{n}{x} \rfloor}{y} \rfloor\),那么用到的状态数只有根据数论分块\(n\)分出的\(\sqrt{n}\)个,那么可以写出递归式

\[T(n)=\sum_{i=2}^n T(\lfloor \frac{n}{i}\rfloor)+\sqrt{n} \]

其中\(\sqrt{n}\)是累加的复杂度. 那么有

\[T(n)=\sum_{i=2}^{\sqrt{n}} (T(i)+T(\lfloor \frac{n}{i}\rfloor))+\sqrt{n} \]

代入并展开一层,出现的和式是高阶小量,可忽略

\[\begin{aligned}T(n)&=\sum_{i=2}^{\sqrt{n}}(\sqrt{i}+\sqrt{\lfloor\frac{n}{i}\rfloor})+\sqrt{n} \\ &\geq \sum_{i=2}^{\sqrt{n}}(2\sqrt{\sqrt{i}\cdot \sqrt{\frac{n}{i}})}+\sqrt{n} \\ &=2\sqrt{\sqrt{n}}+\sqrt{n} \\ &=O(n^{\frac{3}{4}})\end{aligned} \]

注意到这恰好是杜教筛在没有线性预处理的复杂度。由于这个函数无法线性筛,所以不能做到杜教筛的\(O(n^{\frac{2}{3}})\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<map> 
#define maxn 10000000 
using namespace std;
typedef long long ll;
//小的n用数组,大的n用map记录状态,卡常
ll f[maxn+5];
map<ll,ll>mf; 
ll dfs(int n){
	if(n==1) return 1;
	if(n<=maxn&&f[n]) return f[n];
	if(mf.count(n)) return mf[n];  
	ll ans=0; 
	for(int l=2,r;l<=n;l=r+1){
		r=n/(n/l);
		ans+=dfs(n/l)*(r-l+1);
	}
	if(n<=maxn) f[n]=ans;
	else mf[n]=ans;
	return ans;
}
int main(){
#ifndef LOCAL
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
#endif 
	int n; 
	scanf("%d",&n);

	printf("%lld\n",dfs(n));
}
posted @ 2020-12-03 16:41  birchtree  阅读(127)  评论(0编辑  收藏  举报