可以先去树链剖分 - 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的时候弄反了。。