首页 > 其他分享 >树分治

树分治

时间:2024-08-03 19:49:37浏览次数:9  
标签:head int 分治 tot maxn ms

点分治

点分治适合处理大规模的树上路径信息问题。

Luogu P4178 Tree

给定一棵有 \(n\) 个点的带权树,给出 \(k\),询问树上距离小于等于 \(k\) 的点对数量。

分治地考虑每一个子树,求出重心,对答案有贡献的是三种点对,依次解决。

  1. 重心 - 子树上的点

    直接累加贡献即可。

  2. 子树上的点 - 另一个子树上的点

    记录下每个点到重心的距离 \(p\),如果两个点的距离不超过 \(k\),则说明这两个点到重心的距离和不超过 \(k\),即 \(p_u+p_v \le k\)。

    可以把当前子树的 \(p\) 数组排序,双指针求得点对数量,容易发现这对总复杂度产生了一个 \(\log n\) 的贡献。

  3. 子树上的点 - 同一个子树上的点

    这种点对应该在子树中分治地求解,但有些点对可能会产生 \(u\)-重心-\(v\) 形(\(u,v\) 同子树)。

    这时只需删除重复统计的贡献,对子树用一遍双指针删去即可。

yxc 的代码细节值得反复欣赏(进行了些许魔改)。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 5;
int n, m;
int q[maxn], p[maxn];
bool del[maxn];
int head[maxn], tot;
struct node{ int v, w, nxt; } e[maxn];
void add(int u, int v, int w){ e[++tot].v = v, e[tot].w = w, e[tot].nxt = head[u],head[u] = tot; }

int get_size(int u, int fa)
{
	if (del[u]) return 0;
	int ret = 1;
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].v;
		if (v != fa) ret += get_size(v, u);
	}
	return ret;
}

int get_wc(int u, int fa, int siz, int &wc) // 求重心,返回子树大小
{
	if (del[u]) return 0;
	int sum = 1, ms = 0; // 最大子树
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].v;
		if (v == fa) continue;
		int c = get_wc(v, u, siz, wc);
		ms = max(ms, c);
		sum += c;
	}
	ms = max(ms, siz - sum);
	if (ms <= siz / 2) wc = u;
	return sum;
}

void get_dist(int u, int fa, int dist, int &qt) // 求子树上各点到重心的距离
{
	if (del[u]) return;
	q[qt++] = dist;
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].v, w = e[i].w;
		if (v != fa) get_dist(v, u, dist + w, qt);
	}
}

int get(int a[], int len)
{
	sort(a, a + len);
	int res = 0;
	for (int l = -1, r = len - 1; r >= 0; r--)
	{
		while (l + 1 < r && a[l + 1] + a[r] <= m)
			l++;
		l = min(l, r - 1);
		res += l + 1;
	}
	return res;
}

int solve(int u)
{
	if (del[u]) return 0;
	int ans = 0;
	get_wc(u, -1, get_size(u, -1), u);
	del[u] = 1;
	int pt = 0;
	for (int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].v, qt = 0;
		get_dist(v, -1, e[i].w, qt); // 求出子树 j 上各点到重心的距离
		ans -= get(q, qt);			 // *减去子树 j 的内部不合法贡献
		for (int k = 0; k < qt; k++)
		{
			if (q[k] <= m) ans++;	// 加上重心到当前点路径
			p[pt++] = q[k];			// 记录已经搜过的
		}
	}
	ans += get(p, pt); //*隔着重心的点的贡献
	for (int i = head[u]; i; i = e[i].nxt) //*两个端点
	{
		int v = e[i].v;
		ans += solve(v);
	}
	return ans;
}

signed main()
{
	cin >> n;
	for (int i = 1; i <= n - 1; i++)
	{
		int u, v, w;
		cin >> u >> v >> w;
		add(u, v, w), add(v, u, w);
	}
	cin >> m;
	cout << solve(1) << endl;
	return 0;
}

标签:head,int,分治,tot,maxn,ms
From: https://www.cnblogs.com/tai-chi/p/18340957

相关文章

  • cdq分治
    cdq分治主要思想为分治,分为三个部分:左区间内部。左区间对右区间。右区间内部。一个保险的标准顺序是先处理左区间,再处理左区间对右区间的贡献,最后处理右区间,这样就可以保证时序性了。注意这种写法在处理左区间对右区间贡献是要先按标号排序分出正确的左右区间,如果是先递归......
  • 解密编程的八大法宝(四)(附二分查找、分治法和图论算法(深度和广度优先搜索、最短路径、最
    算法题中常见的几大解题方法有以下几种::暴力枚举法(BruteForce):这是最基本的解题方法,直接尝试所有可能的组合或排列来找到答案。这种方法适用于问题规模较小的情况,但在大多数情况下效率不高。贪心算法(GreedyAlgorithm):贪心算法在每一步都选择当前看起来最优的解,希望最终能......
  • 点分治板子
    #define_CRT_SECURE_NO_WARNINGS#include<iostream>#include<vector>#include<algorithm>#include<math.h>#include<sstream>#include<string>#include<string.h>#include<iomanip>#include<stdlib.h>#inc......
  • 线段树分治小结
    一般来说,这种题目是给定一些信息,比如说边,然后这些信息会在一个时间段内出现。一般来说需要离线,然后建立一棵以维护每个时刻信息的线段树,具体来说就是在每个节点维护一个vector。#121.「离线可过」动态图连通性以经典例题来说,我们根据每条边出现的时刻将它插入到线段树上对应时......
  • 树分治、动态树分治学习笔记
    点分治点分治适合处理大规模的树上路径信息问题,选取重心,将当前的树拆分为几颗子树,然后递归子树求解问题,但是今天的重点不在这里边分治与点分治类似,选取一条边,均匀地将树分成两个部分,但是对于一个点有多个儿子时,时间复杂度就会非常大,于是我们可以将其转化,这里有两种方法\(1.\)......
  • 归并分治_小和问题
    归并分治原理:1)思考一个问题在大范围上的答案,是否等于,左部分的答案+右部分的答案+跨越左右产生的答案2)计算“跨越左右产生的答案”时,如果加上左、右各自有序这个设定,会不会获得计算的便利性3)如果以上两点都成立,那么该问题很可能被归并分治解决4)求解答案的过程中只需......
  • C141 线段树分治+线性基 P3733 [HAOI2017] 八纵八横
    视频链接:C141线段树分治+线性基P3733[HAOI2017]八纵八横_哔哩哔哩_bilibili   P3733[HAOI2017]八纵八横-洛谷|计算机科学教育新生态(luogu.com.cn)//线段树分治+线性基O(q*logq*logL*logL)#include<iostream>#include<cstring>#include<algorithm>......
  • cdq分治 提高篇
    优化动态规划序列首先要会最长上升子序列的转移,这里就不说了。我们\(i\)位置的初始值为\(a_i\),可能变成的最大值为\(mx_i\),可能变成的最小值为\(mn_i\)。然后如果\(j\)要转移到\(i\),则需要满足:\(j<i,mx_j\lea_i,a_j\lemn_i\)。然后考虑把\([l,mid]\)按照\(mx\)......
  • cdq分治
    简介前置芝士:归并排序。\(cdq\)分治是个离线算法,可以解决三维偏序或者优化\(dp\)。题目直接上个题目:陌上花开。维护三维偏序有个口诀:一维排序,二维归并,三位数据结构。考虑第一维直接排序解决掉,然后还剩两维。我们考虑第二维用归并排序解决掉。然后假设当前区间\([l,r]\),......
  • 离线分治算法:cdq 分治
    \(\texttt{0x00}\):前置芝士归并排序;树状数组;重载运算符(这个大概都会吧)。\(\texttt{0x01}\):介绍cdq分治是一种离线分治算法,可用来处理以下几种问题:解决和点对有关的问题。1D动态规划的优化与转移。通过CDQ分治,将一些动态问题转化为静态问题。它们的本质都是通过一......