点分治

点分治

点分治完全可以被称为一种优雅的暴力。

引入

统计一棵树上距离等于 \(k\) 的点对数量。

容易想到从上到下枚举每一个节点 \(u\),合并 \(u\) 子树内的答案与经过 \(u\) 的答案,在极限情况下(链),时间复杂度达到 \(O(n^2)\)

为什么会超时,显然对于每一个深度,它枚举的 \(n\) 个点,而一共会枚举 \(n\) 个深度。

考虑对深度进行优化(事实上优化不了前点数),让每一层都很“均匀”,很容易想到重心

依据重心分割子树求解,这就是点分治的本质。

时间复杂度

类比启发式合并,这样的”启发式分裂“,时间复杂度证明类似。

依据重心的定义,设母树大小 \(sz_u\) ,每一次分割出的子树大小最大为 \(\cfrac{sz_u}{2}\),即最多分割 \(log_2n\) 层。

时间复杂度为 \(O(nlogn)\)

实现

点分治主函数

这是点分治的模板,几乎不会变。

void dfs(int u) { // 点分治主函数
  vis[u] = 1,calc(u); // 统计以u为根的子树的答案
  for (edge e : G[u]) {
    int v = e.to;
    if (vis[v]) continue;
    get_sz(v,0); // 取得子树大小
    sum = sz[v],rt = 0; 
    getCentroid(v,u); // 找重心
    dfs(rt);
  }
}

找重心

采用树形DP的方式解决(严格重心)。也可以从根向下沿着 \(sz\) 大的子树找(不一定是严格意义的重心,但时间复杂度也有保证)。

int sz[maxn + 5],mx[maxn + 5],sum = 0;
void getCentroid(int u,int fa) { // 从 u 出发的分割子树的重心
  mx[u] = 0,sz[u] = 1;
  for (edge e : G[u]) {
    int v = e.to;
    if (v == fa || vis[v]) continue;
    getCentroid(v,u);
    sz[u] += sz[v];
    mx[u] = max(mx[u],sz[v]);
  }
  mx[u] = max(mx[u],sum - sz[u]);
  if (mx[u] < mx[rt]) rt = u;
}

统计函数

这是所有点分治问题中最重要的一环(类比线段树中 push_up() 的地位),以下以统计等于 \(k\) 的路径条数为例。

int ans = 0; // 答案
int f[maxn + 5];
int depth[maxn + 5]; // 子树内每个节点到达u的路径长度(子树内深度)
int dis[maxn + 5],cnt = 0;
int cur[maxn + 5],tot = 0;
void getDis(int u,int fa) { // 计算到达 u 的路径->dis[v]
  for (edge e : G[u]) {
    int v = e.to,w = e.w;
    if (vis[v] || v == fa) continue;
    depth[v] = depth[u] + w;
    dis[++ cnt] = depth[v];
    getDis(v,u);
  }
}
int tmp[maxd + 5]; // 在子树[1,i-1]中距离为j的子树有多少
void calc(int u) { // 统计经过重心u的合法路径
  tot = 0; tmp[0] = 1; // 端点为u
  for (edge e : G[u]) {
    int v = e.to,w = e.w;   
    if (vis[v]) continue;
    depth[v] = w,cnt = 0;
    getDis(v,u);
    for (int i = 1;i <= cnt;i ++) if (k >= dis[i]) ans += tmp[k - dis[i]]; // 匹配
    for (int i = 1;i <= cnt;i ++) cur[++ tot] = dis[i],tmp[dis[i]] ++;
  }
  for (int i = 1;i <= tot;i ++) tmp[cur[i]] = 0;
}

这是不用容斥的写法,按顺序枚举子树,天生不重复。

注意到清空历史路径长度统计数组 tmp 时进行优化,只清除使用过的点 cur

posted @ 2026-07-19 21:53  SilverWolf_lhh  阅读(3)  评论(0)    收藏  举报