题目链接

题意:树链剖分模板题 (树链剖分+线段树)


准备工作:

第一步: 定义声明

// 链式前向星存图 (**** 记得开2倍空间 ****)
struct EDGE
{int to;int next;
}edge[MAXX<<1];// 线段树结构体,存和、lazy标记 (**** 记得开4倍空间 ****)
struct NODE
{int mark;int sum;
}no[MAXX << 2];int head[MAXX<<1],a[MAXX]; int pre[MAXX],deep[MAXX],sz[MAXX],son[MAXX];int top[MAXX],id[MAXX],rk[MAXX];
// 以上是用到的数组,下面代码中有解释// 加边
void add(int be,int en,int i)
{edge[i].to = en;edge[i].next = head[be];head[be] = i;
}

第二步:    DFS1(int u,int fa,int dp)

u代表当前结点

fa代表当前节点的父亲节点

dp代表当前节点的深度

需要完成的工作:

  • 标记每个点的深度 deep[]
  • 标记每个点的父亲 pre[]
  • 标记每个非叶子节点的子树大小(含它自己) sz[]
  • 标记每个非叶子节点的重儿子 son[]
void DFS1(int u,int fa,int dp)
{pre[u] = fa;   // 父亲节点deep[u] = dp;  // 深度sz[u] = 1;     // 每个结点的子树大小起初都为1,包括自己int maxx = -1;for(int i=head[u]; i!=-1; i=edge[i].next){int v = edge[i].to;  // 与u相连边的终点if(v == fa)      // 这里需要判断一下,因为一开始是从 fa - u 过来的continue;DFS1(v,u,dp+1);    // u的子树是v,是遍历v,同时v的深度在u的基础上加1sz[u] += sz[v];    // u的子树大小要加上其孩子节点的子树大小if(sz[v] > maxx){  // 找出重儿子maxx = sz[v];son[u] = v;}}
}

第三步:    DFS2(int u,int tp)

u代表当前结点

tp代表当前结点所在链的顶端结点

需要完成的工作:

  • 标记每个点的新编号  id[]
  • 记录当前标号在树中对应的结点 rk[]
  • 处理每个点所在链的顶端  top[]
  • 处理每条链

顺序:先处理重儿子再处理轻儿子

// 按先重链后轻链的顺序处理树,赋上新编号
void DFS2(int u,int tp)
{top[u] = tp;  // 赋值顶端结点id[u] = cnt; // 当前节点的新编号为 cntrk[cnt++] = u; // 编号cnt对应的结点为uif(!son[u])  // 没有儿子就返回return ;DFS2(son[u],tp);  // 先搜索重儿子for(int i=head[u]; i!=-1; i=edge[i].next){int v = edge[i].to;if(v == pre[u] || v == son[u])continue;DFS2(v,v); // 轻儿子}
}

第四步:   Build(1,n,1)

区间 [1,n] ,起始编号 n

// 线段树区间更新的建树过程void Build(int l,int r,int i)
{if(l == r){no[i].sum = a[rk[l]]%mod; // 记录和no[i].mark = 0;       // lazy 标记return ;}int mid = (l+r) >> 1;Build(l,mid,i<<1);Build(mid+1,r,i<<1|1);PushUp(i);    // 随时更新该点信息
}

操作过程:

1 x y z: 对 [x,y] 加z

用到树链剖分,可以利用 top[] 来加快的确定 x y 在线段树中的下标

设所在链顶端 (top[x]) 的深度 (deep[top[x]]) 更深的那个点为x点

  • ans加上 x点到x所在链顶端 (id[top[x]] - id[x] ) 这一段区间的点权和
  • 把x跳到x所在链顶端的那个点的上面一个点

不停执行这两个步骤,直到两个点处于一条链上,这时再加上此时两个点的区间和即可

这时我们注意到,我们所要处理的所有区间均为连续编号(新编号),于是想到线段树,用线段树处理连续编号区间和

// 线段树的更新操作
void Update(int l,int r,int i,int ll,int rr,int w)
{if(ll <= l && r <= rr){no[i].mark = (no[i].mark%mod + w%mod)%mod;no[i].sum = (no[i].sum%mod + (w%mod*((r-l+1)%mod)%mod))%mod;return ;}int mid = (l+r) >> 1;PushDown(i,mid-l+1,r-mid);  // 下放延迟标记if(ll <= mid)Update(l,mid,i<<1,ll,rr,w);if(rr > mid)Update(mid+1,r,i<<1|1,ll,rr,w);PushUp(i);  // 更新该点信息
}void Rupdate(int x,int y,int w)
{while(top[x] != top[y]){     // 如果两个不是在同一条链上int dp1 = deep[top[x]];  // 求两个顶端链的深度int dp2 = deep[top[y]];//  △if(dp1 >= dp2){  // 如果x的顶端链的深度更大,则求 [x,top[x] ] 这一段区间的和Update(1,n,1,id[top[x]],id[x],w); // id[top[x]] < id[x]x = pre[top[x]];              // x跳到顶端链的父亲节点处} else {   // 如果y的顶端链深度更大,进行类似的操作Update(1,n,1,id[top[y]],id[y],w);y = pre[top[y]];}}// 虽然两个位于同一条链,但不一定是同一个点,还需要加上这一段区间的和// 区间从编号小的开始if(id[x] <= id[y])Update(1,n,1,id[x],id[y],w);elseUpdate(1,n,1,id[y],id[x],w);
}/*
if(num == 1) Rupdate(be,en,c);
*/

例如:

如果我们要处理从节点6到节点5的操作,会发现节点5所在链的顶端正好与节点6所在链的中间相连。

如果我们不加上△处,很可能就会跳到其它无关紧要的节点上,并且陷入死循环

△处,这句话就是为了处理当x,y跳到了同一条链上的时候该如何处理

①: Update 5这一点( id[top[5]] = 5, id[5] = 5,top[5] = 5), 5跳到结点2处

②:发现在同一条链上,处理[2,6] 之间

2 x y : 输出 x 到 y 路径上的和

原理与区间修改类似

// 线段树的区间查询int Query(int l,int r,int i,int ll,int rr)
{int flag = 0;int ans = 0;if(ll <= l && r <= rr){return no[i].sum%mod;}int mid = (l+r) >> 1;PushDown(i,mid-l+1,r-mid); if(ll <= mid){flag = Query(l,mid,i<<1,ll,rr); // 本题需要取模ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;}if(rr > mid){flag = Query(mid+1,r,i<<1|1,ll,rr);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;}ans %= mod;if(ans < 0) ans += mod;return ans;}// 确定 x,y的下表
int Rquery(int x,int y)
{int ans = 0;int flag = 0;// 以下过程与区间更新类似while(top[x] != top[y]){int dp1 = deep[top[x]];int dp2 = deep[top[y]];if(dp1 >= dp2){flag = Query(1,n,1,id[top[x]],id[x]);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;x = pre[top[x]];} else {flag = Query(1,n,1,id[top[y]],id[y]);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;y = pre[top[y]];}}flag = 0;if(id[x] <= id[y]){flag = Query(1,n,1,id[x],id[y]);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;} else{flag = Query(1,n,1,id[y],id[x]);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;}return ans;
}

3 x z : 以 x 为根节点的子树都加上 z

我们可以知道x的下标,x的所有子树大小为 sz[x]

在 DFS2 中,每个子树的新编号都是连续的,可以直接用线段树查询

则我们可以确定查询的区间 [ id[x] , id[x] + sz[x] - 1 ]

Update(1,n,1,id[be],id[be]+sz[be]-1,c);

4 x  : 输出以x为根节点的所有子树和

原理类似于上一部分

Query(1,n,1,id[be],id[be]+sz[be]-1);

完整代码:

题目链接题意:树链剖分模板题 (树链剖分+线段树)// luogu-judger-enable-o2
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define memset(a,n) memset(a,n,sizeof(a))
#define INF 0x3f3f3f3f
using namespace std;
typedef long long LL;
const int MAXX = 1e5+10;
int mod,cnt,n;struct EDGE
{int to;int next;
}edge[MAXX<<1];
struct NODE
{int mark;int sum;
}no[MAXX << 2];
int head[MAXX<<1],a[MAXX];
int pre[MAXX],deep[MAXX],sz[MAXX],son[MAXX];
int top[MAXX],id[MAXX],rk[MAXX];void add(int be,int en,int i)
{edge[i].to = en;edge[i].next = head[be];head[be] = i;
}void PushUp(int i)
{no[i].sum =  (no[i<<1].sum%mod + no[i<<1|1].sum%mod)%mod;if(no[i].sum < 0) no[i].sum += mod;
}
void PushDown(int i,int llen,int rlen)
{if(no[i].mark){no[i<<1].mark = (no[i].mark%mod + no[i<<1].mark%mod)%mod;no[i<<1|1].mark = (no[i].mark%mod + no[i<<1|1].mark%mod)%mod;no[i<<1].sum = ((no[i].mark*llen)%mod + no[i<<1].sum%mod)%mod;no[i<<1|1].sum = ((no[i].mark*rlen)%mod + no[i<<1|1].sum%mod)%mod;no[i].mark = 0;}
}
void Build(int l,int r,int i)
{if(l == r){no[i].sum = a[rk[l]]%mod;no[i].mark = 0;return ;}int mid = (l+r) >> 1;Build(l,mid,i<<1);Build(mid+1,r,i<<1|1);PushUp(i);
}
void Update(int l,int r,int i,int ll,int rr,int w)
{if(ll <= l && r <= rr){no[i].mark = (no[i].mark%mod + w%mod)%mod;no[i].sum = (no[i].sum%mod + (w%mod*((r-l+1)%mod)%mod))%mod;return ;}int mid = (l+r) >> 1;PushDown(i,mid-l+1,r-mid);if(ll <= mid)Update(l,mid,i<<1,ll,rr,w);if(rr > mid)Update(mid+1,r,i<<1|1,ll,rr,w);PushUp(i);
}
int Query(int l,int r,int i,int ll,int rr)
{int flag = 0;int ans = 0;if(ll <= l && r <= rr){return no[i].sum%mod;}int mid = (l+r) >> 1;PushDown(i,mid-l+1,r-mid);if(ll <= mid){flag = Query(l,mid,i<<1,ll,rr);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;}if(rr > mid){flag = Query(mid+1,r,i<<1|1,ll,rr);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;}ans %= mod;if(ans < 0) ans += mod;return ans;}
void DFS1(int u,int fa,int dp)
{pre[u] = fa;deep[u] = dp;sz[u] = 1;int maxx = -1;for(int i=head[u]; i!=-1; i=edge[i].next){int v = edge[i].to;if(v == fa)continue;DFS1(v,u,dp+1);sz[u] += sz[v];if(sz[v] > maxx){maxx = sz[v];son[u] = v;}}
}void DFS2(int u,int tp)
{top[u] = tp;id[u] = cnt;rk[cnt++] = u;if(!son[u])return ;DFS2(son[u],tp);for(int i=head[u]; i!=-1; i=edge[i].next){int v = edge[i].to;if(v == pre[u] || v == son[u])continue;DFS2(v,v);}
}
void Rupdate(int x,int y,int w)
{while(top[x] != top[y]){int dp1 = deep[top[x]];int dp2 = deep[top[y]];if(dp1 >= dp2){Update(1,n,1,id[top[x]],id[x],w);x = pre[top[x]];} else {Update(1,n,1,id[top[y]],id[y],w);y = pre[top[y]];}}if(id[x] <= id[y])Update(1,n,1,id[x],id[y],w);elseUpdate(1,n,1,id[y],id[x],w);
}
int Rquery(int x,int y)
{int ans = 0;int flag = 0;while(top[x] != top[y]){int dp1 = deep[top[x]];int dp2 = deep[top[y]];if(dp1 >= dp2){flag = Query(1,n,1,id[top[x]],id[x]);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;x = pre[top[x]];} else {flag = Query(1,n,1,id[top[y]],id[y]);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;y = pre[top[y]];}}flag = 0;if(id[x] <= id[y]){flag = Query(1,n,1,id[x],id[y]);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;} else{flag = Query(1,n,1,id[y],id[x]);ans = (ans%mod + flag%mod)%mod;if(ans < 0) ans += mod;}return ans;
}
int main()
{int m,root,be,en,c;memset(head,-1);memset(son,0);cin >> n >> m >> root >> mod;for(int i=1; i<=n; i++)cin >> a[i];int num = 1;for(int i=1; i<n; i++){cin >> be >> en;add(be,en,num++);add(en,be,num++);}cnt = 1;DFS1(root,0,1);DFS2(root,root);Build(1,n,1);num = 0;int ans;while(m--){ans = 0;cin >> num;if(num == 1){cin >> be >> en >> c;c %= mod;Rupdate(be,en,c);} else if(num == 2){cin >> be >> en;c %= mod;ans = Rquery(be,en);ans %= mod;if(ans < 0) ans += mod;cout << ans << endl;} else if(num == 3){cin >> be >> c;Update(1,n,1,id[be],id[be]+sz[be]-1,c);} else {cin >> be;ans = Query(1,n,1,id[be],id[be]+sz[be]-1);ans %= mod;if(ans < 0) ans += mod;cout << ans << endl;}}
}

树链剖分代码(洛谷3384)相关推荐

  1. 【树链剖分】洛谷树(P3401)

    正题 P3401 题目大意 给你一棵树,让你进行以下操作 修改一条边的边权 查询一条路径的所有子路径异或值的和 解题思路 记下所有点到根节点的路径亦或值,那么查询就是所有点对的异或值之和 因为边权&l ...

  2. 洛谷3384(树链剖分模板题)

    题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式 ...

  3. 洛谷3384:【模板】树链剖分——题解

    https://www.luogu.org/problemnew/show/P3384 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 ...

  4. 洛谷 P3384 【模板】树链剖分-树链剖分(点权)(路径节点更新、路径求和、子树节点更新、子树求和)模板-备注结合一下以前写的题目,懒得写很详细的注释...

    P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节 ...

  5. ⌈洛谷1505⌋⌈BZOJ2157⌋⌈国家集训队⌋旅游【树链剖分】

    题目链接 [洛谷] [BZOJ] 题目描述 Ray 乐忠于旅游,这次他来到了T 城.T 城是一个水上城市,一共有 N 个景点,有些景点之间会用一座桥连接.为了方便游客到达每个景点但又为了节约成本,T ...

  6. 专题·树链剖分【including 洛谷·【模板】树链剖分

    初见安~~~终于学会了树剖~~~ [兴奋]当初机房的大佬在学树剖的时候我反复强调过:"学树剖没有前途的!!!" 恩.真香. 一.重链与重儿子 所谓树剖--树链剖分,就是赋予一个链的 ...

  7. 洛谷P2486 [SDOI2011]染色(树链剖分+线段树判断边界)

    [题目链接] [思路]: 涉及到树上区间修改操作,所以使用树链剖分,涉及到区间查询,所以使用线段树. update操作时,就正常操作,难点在于query操作的计数. 因为树链剖分的dfs序只能保证一条 ...

  8. 洛谷P4482 [BJWC2018]Border 的四种求法 字符串,SAM,线段树合并,线段树,树链剖分,DSU on Tree...

    原文链接https://www.cnblogs.com/zhouzhendong/p/LuoguP4482.html 题意 给定一个字符串 S,有 q 次询问,每次给定两个数 L,R ,求 S[L.. ...

  9. 洛谷2486 【SDOI2011】染色(线段树+树链剖分)

    传送门 [题目分析] 果然重构才是真理吗..... 因为涉及路径操作,所以考虑树链剖分,线段树维护题意中的: 1.区间颜色段数. 2.区间左右颜色. 3.区间覆盖标记. 因为线段树的问题主要就是考虑p ...

最新文章

  1. UvaUva11059 最大乘积
  2. 高级交叉报表例子程序(C#)中明细列统计数据错误改正!
  3. android image 位移动画_深入理解Android之动画
  4. 如何将业务系统的数据抽取汇聚到数据中台?
  5. ios开发ios9新特性关键字学习:泛型,逆变,协变,__kindof
  6. 【NOI2001】炮兵阵地
  7. 使用docker运行dotnetcore站点
  8. jQuery之全选功能
  9. Vista初级使用技巧及故障总结
  10. slz-servlet的引入
  11. 一条 update 语句引起的事故,这回可以长长记性了
  12. Redis实现MongoDB的getlasterror功能
  13. Android之输入银行卡号判断属于哪个银行
  14. FS FT DTFT DFT关系及频谱分析总结
  15. 两人互殴打架派出所如何处理
  16. 华为云文件夹服务器,华为云空间服务器在哪个文件夹
  17. flask怎么创建虚拟环境_在Windows OS中创建虚拟环境并在本地运行Flask应用程序
  18. Greedy Method
  19. 蚊虫叮咬后的去痒妙方大全
  20. 手机如何当win10电脑摄像头使用

热门文章

  1. 关于PDSCH的码字个数问题
  2. 网站大型服务器价格,大型网站服务器价格
  3. 医学图像多分类的评价指标(包括混淆矩阵,metrics.classification_report等)
  4. 平板世代小孩遇上 Apple II
  5. 机器学习-算法工程师 -面试/笔试准备-重要知识点梳理
  6. Knuth Shuffle
  7. Idea创建控制台程序
  8. 三国霸王大陆服务器维护,霸王大陆BUG一览各位小心注意玩儿霸王时候的操作
  9. 如何做好 H5 性能优化
  10. 关于联想y9000P开独显直连屏幕亮度无法调节问题