[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}\)个,那么可以写出递归式
其中\(\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\n",dfs(n));
}