【模板】树链剖分

树链剖分是一种应付树上修改和查询的算法(数据结构),要求树的形态不发生改变(改变的要用LCT维护)

树剖可以解决如下问题:路径修改(查询),子树修改(查询),单点修改。

其实有的题目DFS序即可,还有的要用点分治会明显方便一些。

本模板支持:输入p,q,查询p,q的路径上的权值和,给定p,w,将p子树权值增加w,单点增加权值(其实怎么搞都好,只要树的形态不改变,修改满足线段树要求)

  1 #include<stdio.h>
  2 #define maxn 1000
  3 struct node{int sum,l,r,laz;};
  4 node seg[6*maxn];
  5 int tot,n,op,fr[maxn],to[maxn],nxt[maxn],w[maxn];
  6 int deep[maxn],fa[maxn],id[maxn],son[maxn],ori[maxn],top[maxn],end[maxn];
  7 void adde(int p,int q){to[++tot]=q;nxt[tot]=fr[p];fr[p]=tot;}
  8 void swp(int &p,int &q){p^=q;q^=p;p^=q;}
  9 int dfs1(int,int);
 10 void dfs2(int,int);
 11 void push(int);
 12 int build(int);
 13 void upd(int,int,int,int);
 14 int que(int,int,int);
 15 int ask(int,int);
 16 int main()
 17 {
 18     scanf("%d%d",&n,&op);
 19     int i,p,q;
 20     for(i=1;i<n;i++)
 21     {scanf("%d%d",&p,&q);adde(p,q);adde(q,p);}
 22     for(i=1;i<=n;i++) scanf("%d",&w[i]);
 23     tot = 0; top[1] = 1; id[1] = ++tot; ori[1] = w[1];
 24     dfs1(1,0); dfs2(1,0);
 25     for(i=1;i<=n;i++) printf("%d ",id[i]); printf("\n");
 26     for(i=1;i<=n;i++) printf("%d ",end[i]); printf("\n");
 27     seg[1].l = 1; seg[1].r = n;
 28     build(1);
 29     //for(i=1;i<30;i++) printf("i %d l %d r %d sum %d\n",i,seg[i].l,seg[i].r,seg[i].sum);
 30     char flag[20];
 31     for(i=1;i<=op;i++)
 32     {
 33         scanf("%s%d%d",flag,&p,&q);
 34         if(flag[0]==‘u‘){upd(1,id[p],end[p],q);}
 35         else if(flag[0]==‘c‘){upd(1,id[p],id[p],q);}
 36         else {printf("%d\n",ask(p,q));}
 37     }
 38     return 0;
 39 }
 40 int dfs1(int now,int f)
 41 {
 42     deep[now] = deep[f]+1; fa[now] = f;
 43     int sum=0,maxs=0,siz,i,t;
 44     for(i=fr[now];i;i=nxt[i])
 45     {
 46         t = to[i];
 47         if(t==f) continue;
 48         siz = dfs1(t,now);
 49         if(siz>maxs){son[now]=t;maxs=siz;}
 50         sum += siz;
 51     }
 52     return sum+1;
 53 }
 54 void dfs2(int now,int f)
 55 {
 56     int s = son[now];
 57     if(s){id[s]=++tot;ori[tot]=w[s];top[s]=top[now];dfs2(s,now);}
 58     int i,t;
 59     for(i=fr[now];i;i=nxt[i])
 60     {
 61         t = to[i];
 62         if(t==s||t==f) continue;
 63         id[t] = ++tot; ori[tot] = w[t]; top[t] = t;
 64         dfs2(t,now);
 65     }
 66     end[now] = tot;
 67 }
 68 void push(int now)
 69 {
 70     if(seg[now].laz)
 71     {
 72         int lz = seg[now].laz; seg[now].laz = 0;
 73         seg[now].sum += lz*(seg[now].r-seg[now].l+1);
 74         if(seg[now].l!=seg[now].r)
 75         {seg[now<<1].laz += lz; seg[(now<<1)|1].laz += lz;}
 76     }
 77 }
 78 int build(int now)
 79 {
 80     node t = seg[now];
 81     if(t.l==t.r){seg[now].sum=ori[t.l];return seg[now].sum;}
 82     int mid = (t.l+t.r)>>1; int lc = now<<1; int rc = lc|1;
 83     seg[lc].l = t.l; seg[lc].r = mid;
 84     seg[rc].l = mid+1; seg[rc].r = t.r;
 85     seg[now].sum  = build(lc)+build(rc);
 86     return seg[now].sum;
 87 }
 88 void upd(int now,int l,int r,int w)
 89 {
 90     push(now);
 91     if(seg[now].l>r||seg[now].r<l) return;
 92     if(l<=seg[now].l&&seg[now].r<=r)
 93     {
 94         seg[now].laz += w; push(now);
 95         return;
 96     }
 97     int lc = now<<1; int rc = lc|1;
 98     upd(lc,l,r,w); upd(rc,l,r,w);
 99     seg[now].sum = seg[lc].sum + seg[rc].sum;
100 }
101 int que(int now,int l,int r)
102 {
103     push(now);
104     if(seg[now].l>r||seg[now].r<l) return 0;
105     if(l<=seg[now].l&&seg[now].r<=r) return seg[now].sum;
106     return que(now<<1,l,r) + que((now<<1)|1,l,r);
107 }
108 int ask(int p,int q)
109 {
110     int ret = 0;
111     int fa1 = top[p]; int fa2 = top[q];
112     while(fa1!=fa2)
113     {
114         if(deep[fa1]<deep[fa2]){swp(fa1,fa2);swp(p,q);}
115         ret += que(1,id[fa1],id[p]);
116         p = fa[fa1];fa1 = top[p];
117     }
118     if(deep[p]<deep[q])    swp(p,q);
119     return ret+que(1,id[q],id[p]);
120 } 

时间: 2024-12-22 06:31:53

【模板】树链剖分的相关文章

[luogu P3384] [模板]树链剖分

[luogu P3384] [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入输出格式 输入格式: 第一行包含4个正整数

luoguP3384 [模板]树链剖分

luogu P3384 [模板]树链剖分 题目 #include<iostream> #include<cstdlib> #include<cstdio> #include<cmath> #include<cstring> #include<iomanip> #include<algorithm> #include<ctime> #include<queue> #define rg register

[模板]树链剖分

原题链接:https://www.luogu.org/problemnew/show/P3384 树链剖分+线段树,备用. 等待补充详细解释中. /* 1 x y z x到y最短路径上加上z 2 x y 求x到y的最短路径上的节点值之和 3 x z 以x为根节点的子树内的所有节点值都加上z 4 x 以x为根节点的所有节点值之和 */ #include<cstdio> void read(int &y) { y=0;char x=getchar();int f=1; while(x<

模板 树链剖分BFS版本

//点和线段树都从1开始 //边使用vector vector<int> G[maxn]; int dfs_clock,que[maxn*2],num[maxn],iii[maxn],b[maxn],a[maxn],top[maxn],deep[maxn],fa[maxn],idx[maxn]; //采用静态链表 //a[i] 是初始时树上每个点的权值 //b[i] 是经过bfs后每个点的权值 //idx[i] 是每个点在全局线段树中的下标 void build_List() { int ft

模板——树链剖分

放个板子而已.. #include<stdio.h> #include<algorithm> #define ls root<<1 #define rs root<<1|1 using namespace std; typedef long long ll; int n,m,r;ll res,p; const int MAXN = 100005; ll a[MAXN]; struct edge{ int v,nxt; }e[MAXN<<1]; s

[模板] 树链剖分找LCA

#include <cstdio> #include <cstring> #define MAX 500005 int d[MAX],fa[MAX],size[MAX],top[MAX],son[MAX]; int N,M,S,tot=0; int head[MAX]; struct edge{ int v,next; }G[MAX<<1]; inline void add(int u,int v){ G[++tot].v=v;G[tot].next=head[u];h

树链剖分[模板](洛谷 P3384)

洛谷·[模板]树链剖分 写在前面 首先,在学树链剖分之前最好先把 LCA.树形DP.DFS序 这三个知识点学了 如果这三个知识点没掌握好的话,树链剖分难以理解也是当然的. 树链剖分 树链剖分 就是对一棵树分成几条链,把树形变为线性,减少处理难度 概念 dfs1() dfs2() 对剖过后的树建线段树 处理问题 概念 重儿子:对于每一个非叶子节点,它的儿子中 儿子数量最多的那一个儿子 为该节点的重儿子 轻儿子:对于每一个非叶子节点,它的儿子中 非重儿子 的剩下所有儿子即为轻儿子 叶子节点没有重儿子

Hdu5044Tree 树链剖分

输入输出挂,扩栈挂,模板树链剖分. #include<iostream> #include<cstdio> #include<cstring> #include<map> #include<vector> #include<stdlib.h> #include<algorithm> using namespace std; const int maxn = 111111; struct Node { int next; i

树链剖分 模板

关于树链剖分 模板: 1 const int MAXN = 20010; 2 struct Edge 3 { 4 int to,next; 5 } edge[MAXN*2]; 6 int head[MAXN],tot; 7 8 int top[MAXN];//top[v]表示v所在的重链的顶端节点 9 int fa[MAXN]; //父亲节点 10 int deep[MAXN];//深度 11 int num[MAXN];//num[v]表示以v为根的子树的节点数 12 int p[MAXN];

luogu3384 【模板】树链剖分

P3384 [模板]树链剖分 题目描述 如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x y 表示求树从x到y结点最短路径上所有节点的值之和 操作3: 格式: 3 x z 表示将以x为根节点的子树内所有节点值都加上z 操作4: 格式: 4 x 表示求以x为根节点的子树内所有节点值之和 输入输出格式 输入格式: 第一行包含4个正整数N.M.R.P,