上一篇文章讲了线段树分裂和合并的模板题,下面加强一下理解
题目链接:https://www.luogu.com.cn/problem/P4556
深绘里一直很讨厌雨天。
灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。
虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。
无奈的深绘里和村民们只好等待救济粮来维生。
不过救济粮的发放方式很特别。
题目描述
首先村落里的一共有 n 座房屋,并形成一个树状结构。然后救济粮分 m 次发放,每次选择两个房屋 (x, y),然后对于 x 到 y 的路径上(含 x 和 y)每座房子里发放一袋 z 类型的救济粮。
然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。
输入格式
输入的第一行是两个用空格隔开的正整数,分别代表房屋的个数 n 和救济粮发放的次数 m。
第 2 到 第 n 行,每行有两个用空格隔开的整数 a, b,代表存在一条连接房屋 a 和 b 的边。
第 (n + 1) 到第 (n+m) 行,每行有三个用空格隔开的整数 x, y, z,代表一次救济粮的发放是从 x 到 y 路径上的每栋房子发放了一袋 z 类型的救济粮。
输出格式
输出 n 行,每行一个整数,第 i 行的整数代表 ii 号房屋存放最多的救济粮的种类,如果有多种救济粮都是存放最多的,输出种类编号最小的一种。
如果某座房屋没有救济粮,则输出 0。
这一道题主要算法有最近公共祖先,线段树合并,其次还有树上差分的思想
主要题意:有一个树形的村庄,每次操作会向x到y的路径的每一户村民发放一袋种类为z的救济粮,最后要求每一户村民家中的数量最多的救济粮的编号,如果数量相同,输出编号较小的
做法:向x到y路径的上每一户村民发放救济粮,可以利用树上差分,给x和y节点的线段树的z救济粮数量加一,x和y的最近公共祖先的线段树的救济粮减1,最近公共祖先的父节点减1,
最后dfs一遍,回溯的时候将子节点的线段树合并到父节点的线段树上去,维护区间最大值即可
那么为什么可以这样做呢,最后可以达到将x到y路径上的每一个点都加上1的效果
代码展示:
#include <iostream> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; typedef pair<int, int> PII; const int N = 100010, M = N * 2; int e[M], ne[M], h[N], idx; int top[N], f[N], son[N], depth[N], sz[N]; int n, m, timestamp, tot; int root[N], ans[N]; struct Node{ int l, r; PII val; }tr[N * 70]; void add(int a, int b) { e[idx] = b, ne[idx] = h[a], h[a] = idx ++; } void dfs1(int u, int fa) { depth[u] = depth[fa] + 1, f[u] = fa, sz[u] = 1; int mxsz = 0; for(int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if(j == fa) continue; dfs1(j, u); sz[u] += sz[j]; if(mxsz < sz[j]) { mxsz = sz[j]; son[u] = j; } } } void dfs2(int u, int t) { top[u] = t; if(son[u]) dfs2(son[u], t); for(int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if(j == f[u] || j == son[u]) continue; dfs2(j, j); } } int lca(int x,int y) //这里我用的是树链刨分求lca,当然也可以用倍增 { while(top[x] != top[y]) { if(depth[top[x]] < depth[top[y]]) swap(x, y); x = f[top[x]]; } return depth[x] < depth[y] ? x : y; } PII max(PII& x, PII& y) { if(x.first > y.first) return x; else if(x.first == y.first) return x.second < y.second ? x : y; else return y; } inline void pushup(int k) { tr[k].val = max(tr[tr[k].l].val, tr[tr[k].r].val); } void modify(int &k, int l,int r,int p,int x) { if(!k) k= ++ tot; if(l==r) { tr[k].val.first += x; tr[k].val.second = p; return; } int m = (l+r)>>1; if(p<=m) modify(tr[k].l, l, m, p, x); else modify(tr[k].r, m+1, r, p, x); pushup(k); } void merge(int &x,int y,int l=1,int r=N) { if(!(x&&y)) x|=y; else if(l==r) tr[x].val.first += tr[y].val.first; else { int m = (l+r)>>1; merge(tr[x].l, tr[y].l, l, m); merge(tr[x].r, tr[y].r, m+1, r); pushup(x); } } void dfs(int u) { for(int i = h[u]; ~i; i = ne[i]) { int j = e[i]; if(j == f[u]) continue; dfs(j); merge(root[u], root[j]); } if(tr[root[u]].val.first) ans[u] = tr[root[u]].val.second; } int main() { cin >> n >> m; memset(h, -1, sizeof h); for(int i = 1; i < n; i ++ ) { int a, b; cin >> a >> b; add(a, b), add(b, a); } dfs1(1, 0); dfs2(1, 0); while(m -- ) { int x, y, z; cin >> x >> y >> z; int anc = lca(x, y); modify(root[x], 1, N, z, 1); modify(root[y], 1, N, z, 1); modify(root[anc], 1, N, z, -1); modify(root[f[anc]], 1, N, z, -1); } dfs(1); for(int i = 1; i <= n; i ++ ) cout << ans[i] << endl; return 0; }
标签:加强版,val,int,线段,tr,分裂,救济粮,root,top From: https://www.cnblogs.com/jay1573/p/16622513.html