@WangYixu
2018-06-19T12:56:54.000000Z
字数 4822
阅读 943
OI 算法 笔记 树剖 线段树
因为有些sb棒极了的出题人,一想到某个操作满足可加性,就开始考各种数据结构来维护这个操作,其中一种考法就是在一棵树上,每个点都有权值,来维护这棵树上的某个链或子树的权值“和”,还要支持什么子树修改啊,链修改啊之类的蛋疼必要操作,为了卡过出题人的毒瘤数据提高我们算法的效率,我们引入树链剖分这一操作。
这里用加法代表那些满足可加性的操作(还有乘法、异或等等)。
树链剖分,就是将一棵树剖分成几条链,变成线性结构,然后就可以跑各种线段树维护了。
为了让树上的链在线性结构上尽量分成较少的链,我们引入树链剖分的关键——轻与重:
上图:

这一步,我们要对树进行dfs,来确定一下几个内容:
dep[]sz[]fa[]son[]code:
LL w[N], son[N], fa[N], dep[N];il LL dfs(LL x, LL f, LL deep) {sz[x] = 1;dep[x] = deep;fa[x] = f;for (register int i = hd[x]; i; i = nxt[i]) if (to[i] != f) {sz[x] += dfs(to[i], x, deep + 1);if (sz[to[i]] > sz[son[x]]) son[x] = to[i];}return sz[x];}
然后,我们对树进行重建,进行树链剖分,在这一步,我们要完成:
id[]wt[]top[]这里,我们要先处理重儿子,再处理轻儿子,这样就能保证每条重链一定在线性结构中连续,而重链又足够长,查询次数可以得到有效降低。
code:
LL wt[N], id[N], sz[N], top[N], ncnt;il void Rebuild(LL x, LL f) { // f is current 'top'id[x] = ++ncnt;wt[ncnt] = w[x];top[x] = f;if (!son[x]) return;Rebuild(son[x], f);for (register int i = hd[x]; i; i = nxt[i]) {if (to[i] != fa[x] && to[i] != son[x]) {Rebuild(to[i], to[i]);}}}
接下来就是修改和查询了,一个一个的讲:
给定一条链的左右端点x,y,将链上的每个节点增加某个值val
设更深的点为x,修改x到重链的顶端的权值。然后令x跳到顶端的父亲节点,重复这一过程,直至x, y在同一条重链中,这时,直接修改区间即可。
code:
// Add is a funcition in SegmentTree,// it can fast add a value to a segmentil void Achain(LL x, LL y, LL val) {val %= p;while (top[x] != top[y]) {if (dep[top[x]] < dep[top[y]]) swap(x, y);Add(1, 1, n, id[top[x]], id[x], val);x = fa[top[x]];}if (id[x] > id[y]) swap(x, y);Add(1, 1, n, id[x], id[y], val);}
给定一条链的左右端点x,y,返回链上的每个节点的权值和。
做法类似于修改。
code:
// Sum is a funcition in SegmentTree,// it can fast return the sum of a segmentil LL Qchain(LL x, LL y) {LL ans = 0;while (top[x] != top[y]) {if (dep[top[x]] < dep[top[y]]) swap(x, y);ans = (ans + Sum(1, 1, n, id[top[x]], id[x])) % p;x = fa[top[x]];}if (id[x] > id[y]) swap(x, y);ans = (ans + Sum(1, 1, n, id[x], id[y])) % p;printf("%lld\n", ans);}
使某个点及其子树的权值增加val。
由于一棵子树在线性结构是连续的,直接对区间操作即可。
code:
il void Ason(LL rt, LL val) {Add(1, 1, n, id[rt], id[rt] + sz[rt] - 1, val % p);}il LL Qson(LL rt) {printf("%d\n", Sum(1, 1, n, id[rt], id[rt] + sz[rt] - 1) % p);}
codes:
#include<cctype>#include<cstdio>#include<algorithm>using std::swap;#define il inlinetypedef long long LL;const int N = 100000 + 10;const int M = (N - 1) * 2 + 10;LL hd[N], to[M], nxt[M], cnt; // TreeLL w[N], son[N], fa[N], dep[N]; // Basic InfoLL wt[N], id[N], sz[N], top[N], ncnt; // Tree Chain SplitLL st[N << 2], add[N << 2]; // Segment TreeLL n, m, r, p; // Prob Info//---SegmentTree---//il LL ls(LL x) { return x << 1; }il LL rs(LL x) { return (x << 1) | 1; }il void PushDown(LL rt, LL len) {if (!add[rt]) return;(st[ls(rt)] += add[rt] * (len - (len >> 1)) % p) %= p;(st[rs(rt)] += add[rt] * (len >> 1) % p) %= p;(add[ls(rt)] += add[rt]) %= p;(add[rs(rt)] += add[rt]) %= p;add[rt] = 0;}il void Build(LL rt, LL l, LL r) {if (l == r) { st[rt] = wt[l]; return; }LL mid = (l + r) >> 1;Build(ls(rt), l, mid);Build(rs(rt), mid + 1, r);st[rt] = st[ls(rt)] + st[rs(rt)];}il void Add(LL rt, LL l, LL r, LL ql, LL qr, LL val) {if (ql <= l && qr >= r) {add[rt] = (add[rt] + val) % p;st[rt] = (st[rt] + (r - l + 1) * val) % p;return;}PushDown(rt, r - l + 1);LL mid = (l + r) >> 1;if (ql <= mid) Add(ls(rt), l, mid, ql, qr, val);if (qr > mid) Add(rs(rt), mid + 1, r, ql, qr, val);st[rt] = (st[ls(rt)] + st[rs(rt)]) % p;}il LL Sum(LL rt, LL l, LL r, LL ql, LL qr) {if (ql <= l && qr >= r) {return st[rt] % p;}PushDown(rt, r - l + 1);LL mid = (l + r) >> 1, ans = 0;if (ql <= mid) ans = (ans + Sum(ls(rt), l, mid, ql, qr)) % p;if (qr > mid) ans = (ans + Sum(rs(rt), mid + 1, r, ql, qr)) % p;return ans;}//---End---////---树链剖分---//il LL dfs(LL x, LL f, LL deep) {sz[x] = 1;dep[x] = deep;fa[x] = f;for (register int i = hd[x]; i; i = nxt[i]) if (to[i] != f) {sz[x] += dfs(to[i], x, deep + 1);if (sz[to[i]] > sz[son[x]]) son[x] = to[i];}return sz[x];}il void Rebuild(LL x, LL f) {id[x] = ++ncnt;wt[ncnt] = w[x];top[x] = f;if (!son[x]) return;Rebuild(son[x], f);for (register int i = hd[x]; i; i = nxt[i]) {if (to[i] != fa[x] && to[i] != son[x]) {Rebuild(to[i], to[i]);}}}il void Achain(LL x, LL y, LL val) {val %= p;while (top[x] != top[y]) {if (dep[top[x]] < dep[top[y]]) swap(x, y);Add(1, 1, n, id[top[x]], id[x], val);x = fa[top[x]];}if (id[x] > id[y]) swap(x, y);Add(1, 1, n, id[x], id[y], val);}il LL Qchain(LL x, LL y) {LL ans = 0;while (top[x] != top[y]) {if (dep[top[x]] < dep[top[y]]) swap(x, y);ans = (ans + Sum(1, 1, n, id[top[x]], id[x])) % p;x = fa[top[x]];}if (id[x] > id[y]) swap(x, y);ans = (ans + Sum(1, 1, n, id[x], id[y])) % p;printf("%lld\n", ans);}il void Ason(LL rt, LL val) {Add(1, 1, n, id[rt], id[rt] + sz[rt] - 1, val % p);}il LL Qson(LL rt) {printf("%d\n", Sum(1, 1, n, id[rt], id[rt] + sz[rt] - 1) % p);}//---End---////---BasicOperate---//char c; LL tmp;il LL read() {while (!isdigit(c = getchar()));tmp = c ^ '0';while (isdigit(c = getchar())) tmp = (tmp << 3) + (tmp << 1) + (c ^ '0');return tmp;}il void adde(int x, int y) {cnt++;to[cnt] = y;nxt[cnt] = hd[x];hd[x] = cnt;}//---End---//int main() {n = read(); m = read(); r = read(); p = read();for (register int i = 1; i <= n; ++i) w[i] = read();for (register int i = 1, x, y; i < n; ++i) {x = read(); y = read();adde(x, y); adde(y, x);}dfs(r, 0, 1);Rebuild(r, r);Build(1, 1, n);for (register int i = 1, x, y, z, op; i <= m; ++i) {op = read();switch(op) {case 1 :x = read(); y = read(); z = read();Achain(x, y, z);break;case 2 :x = read(); y = read();Qchain(x, y);break;case 3 :x = read(); z = read();Ason(x, z);break;case 4 :x = read();Qson(x);break;}}return 0;}