预备知识
线段树:https://blog.csdn.net/weixin_53360179/article/details/115528344(这个是我写的)
链式前向星:https://blog.csdn.net/sugarbliss/article/details/86495945(别人的)
https://www.luogu.com.cn/problem/P3384
首先看一下这个模板
所谓树链剖分,就是相近一切办法把树的问题转化为线段树的问题,然后用线段树来解决。
我们先解决比较简单的操作三和操作四,那只要我们将某个点的子树编号化成一个连续的区间,而线段树又恰好是解决连续区间和的问题的利器,那我们如何将子树的编号化为连续的呢???,同时为了确定子树的区间,我们需要知到该子树的大小。
dfs恰好可以解决这个问题,不是吗???在我们dfs某个点的时候,在我们不完成这个点以下的所有点的搜索,我们是不会离开这个点的子树。
const int N = 1e5 + 10;
int to[N], nex[N], head[N];
int dfs[N], fa[N], siz[N];
int idx;//边的序号
void add(int a, int b)//增加一条a到b的有向边
{
to[++idx] = b;//边的终点
nex[idx] = head[a];//同一起点的上一条边
head[a] = idx;//以a为起点的最后一条边
}
int cnt = 0;
void dfs1(int u,int father)
{
siz[u] = 1;
fa[u] = father;
dfs[u] = ++cnt;//dfs序
for(int i = head[u]; i != -1; i = nex[i])
{
int t = to[i];
if(t == fa[u]) continue;
dfs1(t, u);
siz[u] += siz[t];
}
}
再完成序号排列后,我们边可以直接利用线段树的知识解决操作三和操作四。
那我们应当如果解决操作一和操作二,我们同样需要将其转化为线段树的知识,也就是对其排序,如果我们能将最短路径剖分成几个部分,每个部分是一个连续的区间,那么我们可以照常按照线段是的知识解决。
对此我们定义重孩子(孩子所在子树更大的那个孩子,如果两个孩子的大小相同则任取一个,如果只有一个就取该孩子,如果没有孩子则不取),我们在dfs时先遍历重孩子所在的子树,再遍历轻孩子所在的子树,并且由该节点到轻孩子的链设置为轻链,其余的链设置为重链,那我们一直沿着重链的路径便是最短路径。但是我们需要知到哪个是重孩子,所以我们要进行两次dfs。
为了区分出重链和轻链,我们可以设置一个top[]数组来表示重链上深度最小的那个点,那我们还需要一个dep[]数组来储存深度,设要求节点s到e最短路径上的和,我们需要设定s是顶部节点深度大的那个节点,先将s到顶部节点的数加起来,然后将s跳到顶部节点的父亲(对此我们需要设fa[]来表示每个节点的父亲),循环上述过程,知到s,e在同一条重链上,然后求s到e的区间和,将上面所有的和加起来便得到答案。
最短路径加上某一个字的思路也如上。
总结:
dfs1需要求得每颗子树的大小,每个点的深度,每个点的父亲,每个点的重孩子。
dfs2需求的dfs序和顶部节点,求得dfs序的同时可以求得对应点的值
练练模板吧
P3884
题解:
#include <bits/stdc++.h>
using namespace std;
const int N = 200000+10;//取1e5 + 10会爆,因为链式前向星加得是单向边,我们需要双向边,我们也可以只改to[],nex[],head[]数组
int n,m,root,p;
int to[N], nex[N], head[N];
int dfs[N], fa[N], sz[N], dep[N], nd[N], son[N], top[N], d[N];
int idx;//边的序号
void add(int a, int b)//增加一条a到b的有向边
{
to[++idx] = b;//边的终点
nex[idx] = head[a];//同一起点的上一条边
head[a] = idx;//以a为起点的最后一条边
}
int cnt = 0;
void dfs1(int u,int father, int depth)
{
dep[u] = depth;
fa[u] = father;
sz[u] = 1;
for(int i = head[u]; i!= -1; i = nex[i])
{
int t = to[i];
if(t == fa[u]) continue;
dfs1(t, u, depth+1);
sz[u] += sz[t];
if(sz[son[u]] < sz[t]) son[u] = t;//更新重孩子
}
}
void dfs2(int u, int tt)//节点u的顶部节点为tt
{
dfs[u] = ++cnt;
top[u] = tt;
nd[cnt] = d[u];//dfs序为cnt的值
if(!son[u]) return;//没有重孩子
dfs2(son[u], tt);//先搜索重孩子
for(int i = head[u]; i != -1; i = nex[i])
{
int t = to[i];
if(t == son[u] || t == fa[u]) continue;
dfs2(t, t);//轻链开端的顶部就是他自己
}
}
struct node//节点rt所包含的信息
{
int l,r,lz,v;
}tree[N<<2];
void push_up(int rt)
{
tree[rt].v = (tree[rt<<1].v + tree[rt<<1|1].v)%p;
}
void push_down(int rt)
{
if(tree[rt].lz)
{
tree[rt<<1].lz += tree[rt].lz;
tree[rt<<1|1].lz += tree[rt].lz;
tree[rt<<1].v = tree[rt<<1].v + (tree[rt<<1].r - tree[rt<<1].l+1)*tree[rt].lz;
tree[rt<<1|1].v = tree[rt<<1|1].v + (tree[rt<<1|1].r - tree[rt<<1|1].l+1)*tree[rt].lz;
tree[rt].lz = 0;
}
}
void bulid(int rt, int l, int r)
{
tree[rt] = {l, r};
if(l == r)
{
tree[rt].v = nd[l];
return ;
}
int mid = (l + r)>>1;
bulid(rt<<1, l, mid);
bulid(rt<<1|1, mid+1, r);
push_up(rt);
}
void modify(int rt, int l, int r, int k)
{
if(l <= tree[rt].l && tree[rt].r <= r)
{
tree[rt].lz += k;
tree[rt].v = (tree[rt]. v +k * (tree[rt].r - tree[rt].l + 1))%p;
return;
}
push_down(rt);
int mid = (tree[rt].l+tree[rt].r)>>1;
if(l <= mid) modify(rt<<1, l, r, k);
if(mid + 1 <= r) modify(rt<<1|1, l ,r ,k);
push_up(rt);
}
int query(int rt, int l, int r)
{
if(l <= tree[rt].l && tree[rt].r <= r)
{
return tree[rt].v%p;
}
push_down(rt);
int ans = 0;
int mid = (tree[rt].l + tree[rt].r) >> 1;
if(l <= mid) ans = (ans + query(rt<<1, l, r))%p;
if(mid + 1 <= r) ans =( ans + query(rt<<1|1, l, r))%p;
return ans%p;
}
void modify_tree(int u, int k)
{
modify(1, dfs[u], dfs[u] + sz[u] - 1, k);
}
int query_tree(int u)
{
return query(1, dfs[u], dfs[u] + sz[u] - 1)%p;
}
void modify_path(int u, int v, int k)
{
while(top[u] != top[v])//一直跳到u v在同一条重链
{
if(dep[top[u]] < dep[top[v]]) swap(u, v);
modify(1, dfs[top[u]], dfs[u], k);
u = fa[top[u]];
}
if(dep[u] < dep[v]) swap(u , v);
modify(1, dfs[v], dfs[u], k);
}
int query_path(int u, int v)
{
int ans = 0;
while(top[u] != top[v])//一直跳到u v在同一条链
{
if(dep[top[u]] < dep[top[v]]) swap(u, v);
ans = (ans + query(1, dfs[top[u]], dfs[u]))%p;
u = fa[top[u]];
}
if(dep[u] < dep[v]) swap(u, v);
ans = (ans + query(1, dfs[v], dfs[u]))%p;
return ans;
}
int main()
{
memset(head, -1, sizeof(head));
cin>>n>>m>>root>>p;
for(int i = 1; i <= n; ++i)
{
cin>>d[i];
}
for(int i = 1; i <= n-1; ++i)
{
int a,b;
cin>>a>>b;
add(a, b);
add(b, a);
}
dfs1(root, -1, 1);
dfs2(root, root);
bulid(1, 1, n);
for(int i = 1; i <= m; ++i)
{
int op,x,y,z;;
cin>>op;
if(op == 1)
{
cin>>x>>y>>z;
modify_path(x, y, z);
}
else if(op == 2)
{
cin>>x>>y;
cout<<query_path(x, y)%p<<endl;
}
else if(op == 3)
{
cin>>x>>z;
modify_tree(x, z);
}
else if(op == 4)
{
cin>>x;
cout<<query_tree(x)%p<<endl;
}
}
}
如果在阅读过程发现什么问题还请及时告知于我