luogu3384 【模板】 树链剖分
题目大意
已知一棵包含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为根节点的子树内所有节点值之和
基本概念
对路径和子树上的值进行整体操作,我们要用线段树。每个节点有个Id,对应线段树维护的区间(以后简称区间)上的点。Id要满足以下条件:
- 所有子树上的节点的Id构成一段连续的区间。
- 每个节点都属于且只属于一个重链,使得重链上的节点的Id构成一段连续的区间。
对于一个节点u,它的子节点v属于u所在重链当且仅当v是u的孩子中size(子树中元素的个数)最大的。此时v叫做u的重孩子。
连接两个属于不同重链的节点的是轻边,这两个节点的Id之差必大于1。
实现方法
预处理
先Dfs1求出每个节点的Size,深度,并通过Size求出每个节点的重儿子;再Dfs2求出重链(具体表示为每个节点所在链的链头),并按照Dfs序设置每个节点的Id和LastSonId(Dfs到的子树中的最后一个节点的Id)。
子树操作
直接用线段树操作当前节点的Id和LastSonId即可。
路径操作
当两个节点u,v所在重链的链头不同时,令u为所在链头深度较深的节点,用线段树对u的链头的Id和u的Id进行操作,然后u通过与u链头相连的轻边移动到u链头的父亲那里去,如此循环。最后,当u,v所在链头相同时,令u为深度较深的节点,用线段树对v的Id和u的Id操作即可。
注意事项
- 边的数量应当是节点数量的二倍,因为u到v一条边,v到u也一条边。
- 令u为所在链头深度较深的节点,而不是令u为深度较深的节点。
- 路径操作对uv链头不同时的循环完后,即使u==v,也要线段树操作。
- 由于题目中要求取模,所以struct SplitTree中没有一个+号。
#include <cstdio> #include <cstring> #include <cassert> #include <algorithm> using namespace std; const int MAX_NODE = 100010, MAX_EDGE = MAX_NODE*2, MAX_RANGE_NODE = MAX_NODE * 10; int P; #define LOOP(i, n) for(int i=1; i<=n; i++) struct SplitTree { private: #define ModPlus(x, y) ((x)+((y)%P))%P struct Node; struct Edge; struct Node { Node *HeavySon, *Top, *Father; Edge *Head; int Size, Id, LastSonId, Weight, Depth; }_nodes[MAX_NODE], *Root; struct Edge { Edge *Next; Node *From, *To; }*_edges[MAX_EDGE]; int _lastId, _edgeCnt; struct RangeTree { private: int Sum[MAX_RANGE_NODE], PlusTag[MAX_RANGE_NODE]; int TotRange; void PushDown(int cur, int sl, int sr) { if (PlusTag[cur]) { int mid = (sl + sr) / 2; PlusTag[cur * 2] = ModPlus(PlusTag[cur * 2], PlusTag[cur]); PlusTag[cur * 2 + 1] = ModPlus(PlusTag[cur * 2 + 1], PlusTag[cur]); Sum[cur * 2] = ModPlus(Sum[cur * 2], PlusTag[cur] * (mid - sl + 1)); Sum[cur * 2 + 1] = ModPlus(Sum[cur * 2 + 1], PlusTag[cur] * (sr - mid)); PlusTag[cur] = 0; } } void PullUp(int cur) { Sum[cur] = ModPlus(Sum[cur * 2], Sum[cur * 2 + 1]); } void Update(int cur, int sl, int sr, int al, int ar, int value) { if (al <= sl && sr <= ar) { Sum[cur] = ModPlus(Sum[cur], (sr - sl + 1)*value); PlusTag[cur] = ModPlus(PlusTag[cur], value); return; } PushDown(cur, sl, sr); int mid = (sl + sr) / 2; if (al <= mid) Update(cur * 2, sl, mid, al, ar, value); if (ar > mid) Update(cur * 2 + 1, mid + 1, sr, al, ar, value); PullUp(cur); } int Query(int cur, int sl, int sr, int al, int ar) { if (al <= sl&&sr <= ar) return Sum[cur]; PushDown(cur, sl, sr); int mid = (sl + sr) / 2, ans = 0; if (al <= mid) ans = ModPlus(ans, Query(cur * 2, sl, mid, al, ar)); if (ar > mid) ans = ModPlus(ans, Query(cur * 2 + 1, mid + 1, sr, al, ar)); PullUp(cur); return ans; } public: void Update(int l, int r, int value) { Update(1, 1, TotRange, l, r, value); } int Query(int l, int r) { return Query(1, 1, TotRange, l, r); } void Init(int totRange) { memset(Sum, 0, sizeof(Sum)); memset(PlusTag, 0, sizeof(PlusTag)); TotRange = totRange; } }r; Edge *NewEdge() { return _edges[++_edgeCnt] = new Edge(); } void AddEdge(Node *from, Node *to) { Edge *e = NewEdge(); e->From = from; e->To = to; e->Next = e->From->Head; e->From->Head = e; } void Dfs1(Node *cur, Node *father, int depth) { cur->Size = 1; cur->Depth = depth; cur->Father = father; int maxSonSize = 0; for (Edge *e = cur->Head; e; e = e->Next) { if (e->To != father) { Dfs1(e->To, cur, depth + 1); cur->Size += e->To->Size; if (e->To->Size > maxSonSize) { maxSonSize = e->To->Size; cur->HeavySon = e->To; } } } } void Dfs2(Node *cur, Node *top) { cur->Top = top; cur->Id = ++_lastId; r.Update(cur->Id, cur->Id, cur->Weight); if (cur->HeavySon) Dfs2(cur->HeavySon, top); for (Edge *e = cur->Head; e; e = e->Next) if (e->To != cur->HeavySon && e->To != cur->Father) Dfs2(e->To, e->To); cur->LastSonId = _lastId; } void UpdatePath(Node *u, Node *v, int value) { while (u->Top != v->Top) { if (u->Top->Depth < v->Top->Depth) swap(u, v); r.Update(u->Top->Id, u->Id, value); u = u->Top->Father; } if (u->Depth < v->Depth) swap(u, v); r.Update(v->Id, u->Id, value); } int QueryPath(Node *u, Node *v) { int sum = 0; while (u->Top != v->Top) { if (u->Top->Depth < v->Top->Depth) swap(u, v); sum = ModPlus(sum, r.Query(u->Top->Id, u->Id)); u = u->Top->Father; } if (u->Depth < v->Depth) swap(u, v); sum = ModPlus(sum, r.Query(v->Id, u->Id)); return sum; } void UpdateSubTree(Node *cur, int value) { r.Update(cur->Id, cur->LastSonId, value); } int QuerySubTree(Node *cur) { return r.Query(cur->Id, cur->LastSonId); } public: SplitTree(int root, int totNode) { memset(_nodes, 0, sizeof(_nodes)); memset(_edges, 0, sizeof(_edges)); _lastId = _edgeCnt = 0; Root = _nodes + root; r.Init(totNode); } void SetNodeWeight(int id, int w) { _nodes[id].Weight = w; } void Build(int u, int v) { AddEdge(_nodes + u, _nodes + v); AddEdge(_nodes + v, _nodes + u); } void Init() { Dfs1(Root, NULL, 1); Dfs2(Root, Root); } void UpdatePath(int u, int v, int value) { UpdatePath(_nodes + u, _nodes + v, value); } int QueryPath(int u, int v) { return QueryPath(_nodes + u, _nodes + v); } void UpdateSubTree(int u, int value) { UpdateSubTree(_nodes + u, value); } int QuerySubTree(int u) { return QuerySubTree(_nodes + u); } }; int main() { int totNode, rootId, opCnt, w, u, v, op, val; scanf("%d%d%d%d", &totNode, &opCnt, &rootId, &P); static SplitTree g(rootId, totNode); LOOP(i, totNode) { scanf("%d", &w); g.SetNodeWeight(i, w); } LOOP(i, totNode - 1) { scanf("%d%d", &u, &v); g.Build(u, v); } g.Init(); while (opCnt--) { scanf("%d", &op); switch (op) { case 1://UpdatePath scanf("%d%d%d", &u, &v, &val); g.UpdatePath(u, v, val); break; case 2://QueryPath scanf("%d%d", &u, &v); printf("%d\n", g.QueryPath(u, v)); break; case 3://UpdateSubTree scanf("%d%d", &u, &val); g.UpdateSubTree(u, val); break; case 4://QuerySubTree scanf("%d", &u); printf("%d\n", g.QuerySubTree(u)); //printf("100\n"); break; } } return 0; }