[LuoguP4808][CCC 2018]平衡树(数论分块+记忆化搜索)(有复杂度证明)
题面
我们定义「完美平衡树」如下:
每棵完美平衡树都有一个正整数权值。权值为 (1) 的完美平衡树为只含有 (1) 个节点的树。否则,这棵树的权值为 (w(wge2)),则这棵树为一棵含有 (k(2le kle w)) 棵子树的有根树。所有的 (k) 棵子树都必须是相同的,且它的所有 (k) 棵子树必须完全相同,且自身是完美平衡的。
特别地,所有 (k) 棵子树权值必须相同。它们的权值必须为 (leftlfloorfrac{w}{k} ight floor) 。例如,如果一棵权值为 (8) 的完美平衡树有 (3) 棵子树,那么每棵子树的权值为 (2),因为 (2+2+2=6le8)。
给定 (N(1leq n leq 10^9)),求出权值为 (N) 的完美平衡树的数量。
分析
简化版题面.已知(f_1=1,f_n=sum_{i=2}^n f_{lfloorfrac{n}{i} floor}(n geq 2)),求(f_n(n leq 10^9))
首先,可以利用数论分块把(f_{lfloorfrac{n}{i} floor})的枚举优化到(O(sqrt n)),然后注意到(f_n)用到的状态不多,可以用记忆化搜索,于是就跑过了。
时间复杂度(O(n^{frac{3}{4}})),证明如下:
由于(lfloorfrac{n}{xy}
floor=lfloor frac{lfloorfrac{n}{x}
floor}{y}
floor),那么用到的状态数只有根据数论分块(n)分出的(sqrt{n})个,那么可以写出递归式
其中(sqrt{n})是累加的复杂度. 那么有
代入并展开一层,出现的和式是高阶小量,可忽略
注意到这恰好是杜教筛在没有线性预处理的复杂度。由于这个函数无法线性筛,所以不能做到杜教筛的(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
",dfs(n));
}