P3384 【模板】重链剖分/树链剖分

可以先去树链剖分 - OI Wiki上了解下什么是树链剖分,(要先会线段树,链式前向星属于邻接表的升级版),然后去做洛谷的【模板】重链剖分/树链剖分 - 洛谷

————

大佬的C语言模板:https://www.cnblogs.com/chinhhh/p/7965433.html

我的线段树:线段树1板子 区间加-CSDN博客

————

本模板代码目录:

int n, m, r, p;
const int maxn = 1e5+6;
//线段树############################################
template<class T>
class ST//segment tree
{
	struct node
	{
		T val;
		T t;//懒标记//服务后代
		node(T v = 0) :val(v), t(0)
		{}
	};
	int n = a.size();
	vector<T>a;
	vector<node>d;
public:
	void build_tree(int i, int l, int r)
	{
		if (l == r)
		{
			d[i].val = a[l];
			return;
		}
		int mid = l + (r - l) / 2;
		build_tree(i * 2, l, mid);
		build_tree(i * 2 + 1, mid + 1, r);
		d[i].val = d[i * 2].val + d[i * 2 + 1].val;
	}
	void spread(int i, int l, int r, int aiml, int aimr)
	{
		int mid = l + (r - l) / 2;
		if (d[i].t != 0 && l != r)
		{
			d[i * 2].val += d[i].t * (mid - l + 1);
			d[i * 2 + 1].val += d[i].t * (r - mid);
			d[i * 2].t += d[i].t;//可能上上次也没改
			d[i * 2 + 1].t += d[i].t;
			d[i].t = 0;
		}
	}
	T getsum(int l, int r)
	{
		return _getsum(1, 1, n, l, r);
	}
	T _getsum(int i, int l, int r, int aiml, int aimr)
	{
		if (aiml <= l && r <= aimr)//查询区间的子集全部加起来
			return d[i].val;

		//访问
		int mid = l + (r - l) / 2;
		spread(i, l, r, aiml, aimr);

		T ret = 0;
		if (aiml <= mid)
			ret += _getsum(i * 2, l, mid, aiml, aimr);
		if (aimr >= mid + 1)
			ret += _getsum(i * 2 + 1, mid + 1, r, aiml, aimr);
		return ret;

	}
	void update(int l, int r, ll val)
	{
		_update(1, 1, n, l, r, val);//加并挂标记
	}
	void _update(int i, int l, int r, int aiml, int aimr, ll val)
	{
		if (aiml <= l && r <= aimr)
		{
			d[i].val += val * (r - l + 1);
			d[i].t += val;
			return;
		}


		int mid = l + (r - l) / 2;
		spread(i, l, r, aiml, aimr);

		if (aiml <= mid)
			_update(i * 2, l, mid, aiml, aimr, val);
		if (aimr >= mid + 1)
			_update(i * 2 + 1, mid + 1, r, aiml, aimr, val);
		//我们只对叶子更新了,(别多想懒标记)
		d[i].val = d[i * 2].val + d[i * 2 + 1].val;
	}
	ST(vector<T>arr)
	{
		a = arr;
		n = a.size() - 1;
		d = vector<node>(pow(2, (int)log2(n) + 1 + 1) - 1 + 1);
		build_tree(1, 1, n);
	}
	ST(int* arr,int _siz)
	{
		a = vector<T>(_siz);
		for (int i = 0; i < _siz; i++)
			a[i] = arr[i];

		n = a.size() - 1;
		d = vector<node>(pow(2, (int)log2(n) + 1 + 1) - 1 + 1);
		build_tree(1, 1, n);
	}
};

//链式前向星 <邻接表>###############################
int tot = 0;//total 下标
int head[maxn];//这个邻接表的头的位置
int nxt[maxn*2];//下一个点的下标
int node[maxn*2];//每个到达的点的编号
int val[maxn*2];//边权
inline void add(int a, int b, int t = 1)
{
	tot++;
	nxt[tot] = head[a];
	head[a] = tot;
	node[tot] = b;
	val[tot] = t;//a到b的权值
}

int arr[maxn];
//vector<vector<int>>alist;

//树链剖分 重链剖分#################################
int fa[maxn];
int dep[maxn];
int siz[maxn];//子树大小
int ssiz[maxn];//重链大小(没用)
int son[maxn];//重儿子
int top[maxn];//重链顶部
int dfn[maxn];//线段树中编号 ,x的dfs序
int dfval[maxn];
int rnk[maxn];//序号对应哪个x

//1.建树
void tree_decomposition1(int cur)
{
	son[cur] = -1;
	siz[cur] = 1;
	ssiz[cur] = 1;
	for (int i = head[cur]; i; i = nxt[i])
	{
		int aim = node[i];
		if (aim == fa[cur])continue;
		if (dep[aim] == 0)
		{
			dep[aim] = dep[cur] + 1;
			fa[aim] = cur;
			tree_decomposition1(aim);
			siz[cur] += siz[aim];
			if (son[cur] == -1 || siz[aim] > siz[son[cur]])
			{
				son[cur] = aim;
				ssiz[cur] = 1 + ssiz[aim];
			}
		}
	}
}
//开始确定dfs序 tot初始化0!
void tree_decomposition2(int cur, int t/*top*/)
{
	top[cur] = t;
	tot++;
	dfn[tot] = cur;
	rnk[cur] = tot;
	if (son[cur] == -1)return;
	tree_decomposition2(son[cur], t);
	for (int i = head[cur]; i; i = nxt[i])//处理剩余轻边
	{
		int aim = node[i];
		if (aim != son[cur] && aim != fa[cur])
			tree_decomposition2(aim, aim);
	}
}
int td_lca(int u, int v)//不断向上跳重链,当跳到同一条重链上时,深度较小的结点即为 LCA。
{
	while (top[u] != top[v]) 
	{
		if (dep[top[u]] > dep[top[v]])
			u = fa[top[u]];
		else
			v = fa[top[v]];
	}
	return dep[u] > dep[v] ? v : u;
}
int tree_path_sum(int u, int v)
{
	int ret = 0;
	while (top[u] != top[v])
	{
		if (dep[u] < dep[v])
			swap(u, v);
		ret += dep[top[u]] - dep[u];
		u = top[u];
	}
	ret += abs(dep[u] - dep[v]);
	return ret;
}
void td_add_uv(int u,int v,int val, ST<int>&demo)
{
	while (top[u] != top[v])
	{
		if (dep[top[u]] < dep[top[v]])//**重链顶**深的先向上
			swap(u, v);
		demo.update(rnk[top[u]], rnk[u],val);
		u = fa[top[u]];
	}
	if (dep[u] > dep[v])
		swap(u, v);
	demo.update(rnk[u], rnk[v], val);
}
int td_getsum_uv(int u, int v, ST<int>& demo)
{
	int ret = 0;
	while (top[u] != top[v])
	{
		if (dep[top[u]] < dep[top[v]])
			swap(u, v);
		ret += demo.getsum(rnk[top[u]], rnk[u]);
		u = fa[top[u]];
	}
	if (dep[u] > dep[v])
		swap(u, v);
	ret += demo.getsum(rnk[u], rnk[v]);
	return ret%p;
}
void td_add_allSon(int x,int val, ST<int>& demo)//我们有siz哦
{
	demo.update(rnk[x], rnk[x] + siz[x] - 1, val);
}
int td_getsum_allSon(int x, ST<int>& demo)//我们有siz哦
{
	int ret = 0;
	ret += demo.getsum(rnk[x], rnk[x]+siz[x]-1);
	return ret%p;
}

————

自己写时(抄时)犯的错误:

1. 向上跳链用来比较的深度是该点所在重链的头top的深度

在理解上该点的深度似乎也可以,但是最后合并的时候会有问题。

2.(小错误)dfn序和rnk下标 两个在dfs2的时候弄反了。。

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值