[学习笔记] 动态开点权值线段树合并 - 数据结构
权值线段树
例题
【模板】普通平衡树
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 1; int n, val[N], opt[N], num[N], cnt, len, san[N], m[N], rnk[N]; unordered_map<int, int> dfn; struct WeightedSegmentTree{ #define ls (id << 1) #define rs (id << 1 | 1) struct node{ int l, r, num; }t[N<<2]; inline void pushup(int id){ t[id].num = t[ls].num + t[rs].num; } inline void build(int id, int l, int r){ t[id].l = l, t[id].r = r; if(l == r) return; int mid = (l + r) >> 1; build(ls, l, mid), build(rs, mid+1, r); } inline void modify(int id, int u, int v){ if(t[id].l == t[id].r) return (void)(t[id].num += v); int mid = (t[id].l + t[id].r) >> 1; modify(u<=mid?ls:rs, u, v); pushup(id); } inline int query_rank(int id, int th){ if(t[id].l == t[id].r) return rnk[t[id].l]; if(th <= t[ls].num) return query_rank(ls, th); else return query_rank(rs, th-t[ls].num); } inline int query_minnum(int id, int u){ if(t[id].l == t[id].r) return 1; // output the smallest rank int mid = (t[id].l + t[id].r) >> 1; if(u <= mid) return query_minnum(ls, u); else return query_minnum(rs, u) + t[ls].num; } inline int query_maxnum(int id, int u){ if(t[id].l == t[id].r) return t[id].num; // output the biggest rank int mid = (t[id].l + t[id].r) >> 1; if(u <= mid) return query_maxnum(ls, u); else return query_maxnum(rs, u) + t[ls].num; } inline int query_nxt(int id, int u){ return query_rank(1, query_minnum(1, u)-1); } inline int query_lst(int id, int u){ return query_rank(1, query_maxnum(1, u)+1); } }WST; int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n; for(int i=1, op; i<=n; ++i){ cin>>op; opt[i] = op, cin>>num[i]; if(op != 4 && op != 2) val[++cnt] = num[i], san[cnt] = m[cnt] = val[cnt]; } // 以下为离散化 sort(m+1, m+1+cnt); len = unique(m+1, m+1+cnt) - (m+1); for(int i=1; i<=cnt; ++i){ san[i] = lower_bound(m+1, m+1+len, san[i]) - m; dfn[val[i]] = san[i], rnk[san[i]] = val[i]; } WST.build(1, 1, cnt); for(int i=1; i<=n; ++i){ switch(opt[i]){ case 1: WST.modify(1, dfn[num[i]], 1); break; case 2: WST.modify(1, dfn[num[i]], -1); break; case 3: cout<<WST.query_minnum(1, dfn[num[i]])<<'\n'; break; case 4: cout<<WST.query_rank(1, num[i])<<'\n'; break; case 5: cout<<WST.query_nxt(1, dfn[num[i]])<<'\n'; break; case 6: cout<<WST.query_lst(1, dfn[num[i]])<<'\n'; break; } } return 0; }
#include<bits/stdc++.h> using namespace std; const int N = 1e5 + 1; int n, m, f[N], rt[N], q, rnk[N]; inline int find(int k){ if(!f[k]) return k; return f[k] = find(f[k]); } struct WeightedSegmentTree{ int ls[N<<6], rs[N<<6], sum[N<<6], cnt; inline void pushup(int id){ sum[id] = sum[ls[id]] + sum[rs[id]]; } inline void modify(int& id, int l, int r, int u){ if(!id) id = ++cnt; if(l == r) return (void)(sum[id] = 1); int mid = (l + r) >> 1; if(u <= mid) modify(ls[id], l, mid, u); else modify(rs[id], mid+1, r, u); pushup(id); } inline int query(int id, int l, int r, int rk){ if(rk > sum[id]) return -1; if(l == r) return rnk[l]; int mid = (l + r) >> 1; if(rk <= sum[ls[id]]) return query(ls[id], l, mid, rk); else return query(rs[id], mid+1, r, rk-sum[ls[id]]); } inline int merge(int a, int b, int l, int r){ if(!a && !b) return 0; if(!a) return b; if(!b) return a; if(l == r) return a; int mid = (l + r) >> 1; ls[a] = merge(ls[a], ls[b], l, mid); rs[a] = merge(rs[a], rs[b], mid+1, r); pushup(a); return a; } }MST; int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n>>m; for(int i=1, val; i<=n; ++i){ cin>>val, rnk[val] = i; MST.modify(rt[i], 1, n, val); } for(int i=1, a, b; i<=m; ++i){ cin>>a>>b; a = find(a), b = find(b); // cout<<a<<' '<<b<<'\n'; rt[b] = MST.merge(rt[a], rt[b], 1, n); f[b] = a; } // for(int i=1; i<=n; ++i) cout<<rt[i]<<' '; printf("\n"); // printf("%d", rt[find(2)]); // return 0; cin>>q; char opt; for(int i=1, a, b; i<=q; ++i){ cin>>opt>>a>>b; if(opt == 'Q') cout<<MST.query(rt[find(a)], 1, n, b)<<'\n'; else{ a = find(a), b = find(b); if(a == b) continue; f[b] = a; rt[a] = MST.merge(rt[a], rt[b], 1, n); } } return 0; }
[Vani有约会] 雨天的尾巴
考虑对每个点进行动态开点权值线段树。对于树链修改,采用树上差分,最后把每个节点的子树和自己合并起来就是最终答案(树上差分不会可以看我博客)。注意,需要特判这个节点的最大救济粮数量,如果数量为 0,那么救济粮编号也为 0。
#include<bits/stdc++.h> using namespace std; #define min(x,y) (dfn[x] < dfn[y]) ? x : y constexpr int N = 1e5 + 1; int n, m, rt[N], dcnt, st[N][21], dfn[N], ans[N]; struct WeightedSegmentTree{ #define tls t[id].ls #define trs t[id].rs int cnt; struct node{ int ls, rs, mx; }t[N<<6]; inline void pushup(int id){ t[id].mx = max(t[tls].mx, t[trs].mx); } inline void modify(int& id, int l, int r, int u, int k){ if(!id) id = ++cnt; if(l == r) return (void)(t[id].mx += k); int mid = (l + r) >> 1; if(u <= mid) modify(tls, l, mid, u, k); else modify(trs, mid+1, r, u, k); pushup(id); } inline int query(int id, int l, int r){ if(!id) return 0; if(l == r){ if(t[id].mx == 0) return 0; // spj!!! else return l; } int mid = (l + r) >> 1; if(t[tls].mx >= t[trs].mx) return query(tls, l, mid); else return query(trs, mid+1, r); } inline int merge(int a, int b, int l, int r){ if(!a || !b) return a+b; if(l == r){ t[a].mx += t[b].mx; return a; } int mid = (l + r) >> 1; t[a].ls = merge(t[a].ls, t[b].ls, l, mid); t[a].rs = merge(t[a].rs, t[b].rs, mid+1, r); pushup(a); return a; } }MST; vector<int> G[N]; inline void dfs1(int u, int f){ st[dfn[u] = ++dcnt][0] = f; for(int v : G[u]) if(!dfn[v]) dfs1(v, u); } inline int Lca(int u, int v){ if(u == v) return u; if((u = dfn[u]) > (v = dfn[v])) swap(u, v); int k = __lg(v - u++); return min(st[u][k], st[v-(1<<k)+1][k]); } bitset<N> vis; inline void dfs2(int u){ vis[u] = 1; for(int v : G[u]) if(!vis[v]) dfs2(v), rt[u] = MST.merge(rt[u], rt[v], 1, N); ans[u] = MST.query(rt[u], 1, N); // 注意必须这时候统计答案 } int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n>>m; for(int i=1, a, b; i<n; ++i){ cin>>a>>b; G[a].push_back(b), G[b].push_back(a); } dfs1(1, 0); for(int j=1; j<=__lg(n); ++j) for(int i=1; i<=n-(1<<j)+1; ++i) st[i][j] = min(st[i][j-1], st[i+(1<<(j-1))][j-1]); for(int i=1, a, b, c, lca; i<=m; ++i){ cin>>a>>b>>c; lca = Lca(a, b); MST.modify(rt[a], 1, N, c, 1); MST.modify(rt[b], 1, N, c, 1); MST.modify(rt[lca], 1, N, c, -1); MST.modify(rt[st[dfn[lca]][0]], 1, N, c, -1); } dfs2(1); for(int i=1; i<=n; ++i) cout<<ans[i]<<'\n'; return 0; }
蒟蒻的数列
服了,想复杂了。刚开始时在 modify 的同时统计 sum,于是就乱了。这道题需要动态开点维护 lazy_tag
,最后再统计 sum 就非常简单了。
#include<bits/stdc++.h> using namespace std; #define int long long constexpr int N = 1e7 + 1, MAX = 1e9; int n, rt; struct SegmentTree{ int ls[N], rs[N], tag[N], cnt; inline void addtag(int& id, int v){ if(!id) id = ++cnt; tag[id] = max(tag[id], v); } inline void pushdown(int id){ if(tag[id]) addtag(ls[id], tag[id]), addtag(rs[id], tag[id]); tag[id] = 0; } inline void modify(int& id, int x, int y, int l, int r, int k){ if(!id) id = ++cnt; if(l <= x && y <= r){ addtag(id, k); return; } pushdown(id); int mid = (x + y) >> 1; if(l <= mid) modify(ls[id], x, mid, l, r, k); if(r > mid ) modify(rs[id], mid+1, y, l, r, k); } inline int query(int id, int l, int r){ if(!ls[id] && !rs[id]) return tag[id]*(r-l+1); pushdown(id); int mid = (l + r) >> 1, ans = 0; if(ls[id]) ans += query(ls[id], l, mid); if(rs[id]) ans += query(rs[id], mid+1, r); return ans; } }ST; signed main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n; for(int i=1, a, b, c; i<=n; ++i){ cin>>a>>b>>c; if(a <= b-1) ST.modify(rt, 1, MAX, a, b-1, c); } return cout<<ST.query(1, 1, MAX), 0; }
[NOIP2016 提高组] 天天爱跑步
集树上差分与线段树合并一体的好题!看题解看的
首先,我们只需要记录这个点在某个时间点上有没有人即可,权值线段树即可解决。但是又有新的问题:这需要沿着某条树链 ,, 等等,无法差分,并且最后合并的时候子节点的时间数据会加到父节点上,这是错误的。
可以发现,随着时间的推移,节点的深度也在随之变化,那么就可以把时间信息与深度挂钩。对于 的节点,把他们的 ;对于 的节点,把他们的 ,最后统计的时候统计每个节点的 和 的权值即可。最后注意判断 是否等于 0,若等于 0,ans 就会加两次。
#include<bits/stdc++.h> using namespace std; constexpr int N = 3e5, M = 6e5; int n, m, val[N], fa[N], size[N], son[N], dep[N], rt[N], ans[N], top[N]; vector<int> G[N]; inline void dfs1(int u){ size[u] = 1, son[u] = -1; for(int v : G[u]){ if(!dep[v]){ dep[v] = dep[fa[v]=u] + 1; dfs1(v); size[u] += size[v]; if(son[u] == -1 || size[v] > size[son[u]]) son[u] = v; } } } inline void dfs2(int u, int t){ top[u] = t; if(son[u] == -1) return; dfs2(son[u], t); for(int v : G[u]) if(v != son[u] && v != fa[u]) dfs2(v, v); } inline int Lca(int u, int v){ int tu = top[u], tv = top[v]; while(tu != tv){ if(dep[tu] > dep[tv]) tu = top[u=fa[tu]]; else tv = top[v=fa[tv]]; } return dep[u]>dep[v]?v:u; } struct WeightedSegmentTree{ int ls[N<<5], rs[N<<5], sum[N<<5], cnt; inline void modify(int &id, int l, int r, int u, int k){ if(!id) id = ++cnt; if(l == r) return (void)(sum[id] += k); int mid = (l + r) >> 1; if(u <= mid) modify(ls[id], l, mid, u, k); else modify(rs[id], mid+1, r, u, k); } inline int query(int id, int l, int r, int u){ if(!id) return 0; if(l == r) return sum[id]; int mid = (l + r) >> 1; if(u <= mid) return query(ls[id], l, mid, u); else return query(rs[id], mid+1, r, u); } inline int merge(int a, int b, int l, int r){ if(!a || !b) return a+b; if(l == r){ sum[a] += sum[b]; return a; } int mid = (l + r) >> 1; ls[a] = merge(ls[a], ls[b], l, mid); rs[a] = merge(rs[a], rs[b], mid+1, r); sum[a] = sum[ls[a]] + sum[rs[a]]; return a; } }WST; bitset<N> vis; inline void dfs(int u){ vis[u] = 1; for(int v : G[u]) if(!vis[v]) dfs(v), rt[u] = WST.merge(rt[u], rt[v], 1, M<<1); if(val[u]) ans[u] += WST.query(rt[u], 1, M<<1, dep[u]+val[u]+M); ans[u] += WST.query(rt[u], 1, M<<1, dep[u]-val[u]+M); } int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n>>m; for(int i=1, a, b; i<n; ++i) cin>>a>>b, G[a].push_back(b), G[b].push_back(a); for(int i=1; i<=n; ++i) cin>>val[i]; dep[1] = 1, dfs1(1); dfs2(1, 1); for(int i=1, a, b, lca; i<=m; ++i){ cin>>a>>b; lca = Lca(a, b); WST.modify(rt[a], 1, M<<1, dep[a]+M, 1); WST.modify(rt[fa[lca]], 1, M<<1, dep[a]+M, -1); WST.modify(rt[b], 1, M<<1, dep[lca]*2-dep[a]+M, 1); WST.modify(rt[lca], 1, M<<1, dep[lca]*2-dep[a]+M, -1); } dfs(1); for(int i=1; i<=n; ++i) cout<<ans[i]<<' '; return 0; }
[USACO17JAN] Promotion Counting P
不说了,板子。
#include<bits/stdc++.h> using namespace std; constexpr int N = 1e5 + 1, M = 9e6 + 1, MAX = 1e9+1; int n, rt[N], ans[N], val[N]; vector<int> G[N]; struct WeightedSegmentTree{ int cnt, ls[M], rs[M], sum[M]; inline void modify(int &id, int l, int r, int u){ if(!id) id = ++cnt; if(l == r) return (void)(++sum[id]); int mid = (l + r) >> 1; if(u <= mid) modify(ls[id], l, mid, u); else modify(rs[id], mid+1, r, u); sum[id] = sum[ls[id]] + sum[rs[id]]; } inline int query(int id, int l, int r, int u){ if(!id) return 0; if(l == r) return sum[id]; int mid = (l + r) >> 1, ans = 0; if(u <= mid) ans += sum[rs[id]] + query(ls[id], l, mid, u); else ans += query(rs[id], mid+1, r, u); return ans; } inline int merge(int a, int b, int l, int r){ if(!a || !b) return a+b; if(l == r){ sum[a] += sum[b]; return a; } int mid = (l + r) >> 1; ls[a] = merge(ls[a], ls[b], l, mid); rs[a] = merge(rs[a], rs[b], mid+1, r); sum[a] = sum[ls[a]] + sum[rs[a]]; return a; } }WST; bitset<N> vis; inline void dfs(int u){ vis[u] = 1; for(int v : G[u]) if(!vis[v]) dfs(v), rt[u] = WST.merge(rt[u], rt[v], 1, MAX); ans[u] = WST.query(rt[u], 1, MAX, val[u]+1); } int main(){ ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); cin>>n; for(int i=1; i<=n; ++i) cin>>val[i], WST.modify(rt[i], 1, MAX, val[i]); for(int i=2, a; i<=n; ++i) cin>>a, G[i].push_back(a), G[a].push_back(i); dfs(1); for(int i=1; i<=n; ++i) cout<<ans[i]<<'\n'; return 0; }
魔法少女LJJ
好恶心啊,调了我一晚上 + 一下午
对于需要维护 tag 的动态开点线段树,对于这道题了来说,涉及区间修改的只有 把某一区间修改为 0。所以不开点不打 tag 不影响接续的修改,所以没开过的点就不要打 tag 了,会 T。另外开过的点一定打 tag。对于合并操作,不要试图维护 tag,直接 pushdown 即可。
#include<bits/stdc++.h> using namespace std; constexpr int N = 4e5 + 1, M = 1e7 + 1, MAX = 1e9 + 1; int m, tot, num[N], rt[N], f[N]; inline int find(int k){ if(!f[k]) return k; return f[k] = find(f[k]); } struct WeightedSegmentTree{ int ls[M], rs[M], sum[M], cnt=1; double mul[M]; bitset<M> tag; inline void addtag(int &id){ tag[id] = 0; sum[id] = mul[id] = 0; } inline void pushdown(int id){ if(!tag[id]) addtag(ls[id]), addtag(rs[id]); tag[id] = 1; } inline void pushup(int id){ sum[id] = sum[ls[id]] + sum[rs[id]]; mul[id] = mul[ls[id]] + mul[rs[id]]; } inline void modify_itv(int &id, int x, int y, int l, int r){ if(l > r || !id) return; //因为修改为0,所以不开点不打tag不影响接续的修改 if(l <= x && y <= r) return (void)(addtag(id)); //开过的点一定打 tag int mid = (x + y) >> 1; pushdown(id); if(l <= mid) modify_itv(ls[id], x, mid, l, r); if(r > mid) modify_itv(rs[id], mid+1, y, l, r); pushup(id); } inline void modify_nod(int &id, int l, int r, int u, int k){ if(!id) id = ++cnt, tag[id] = 1; if(l == r) return (void)(sum[id]+=k, mul[id]+=log(u)*k); int mid = (l + r) >> 1; pushdown(id); if(u <= mid) modify_nod(ls[id], l, mid, u, k); else modify_nod(rs[id], mid+1, r, u, k); pushup(id); } inline int query_rnk(int id, int l, int r, int k){ if(!id || k > sum[id]) return 0; if(l == r) return l; int mid = (l + r) >> 1; pushdown(id); if(k <= sum[ls[id]]) return query_rnk(ls[id], l, mid, k); else return query_rnk(rs[id], mid+1, r, k-sum[ls[id]]); } inline int query_sum(int id, int x, int y, int l, int r){ if(!id || l > r) return 0; if(l <= x && y <= r) return sum[id]; int mid = (x + y) >> 1, ans = 0; pushdown(id); if(l <= mid) ans += query_sum(ls[id], x, mid, l, r); if(r > mid) ans += query_sum(rs[id], mid+1, y, l, r); return ans; } inline int merge(int a, int b, int l, int r){ if(!a || !b) return a+b; //²»ÒªºÏ²¢tag ûÓÐÓà if(l == r){ sum[a] += sum[b]; return a; } int mid = (l + r) >> 1; pushdown(a), pushdown(b); sum[a] += sum[b], mul[a] += mul[b]; ls[a] = merge(ls[a], ls[b], l, mid); rs[a] = merge(rs[a], rs[b], mid+1, r); return a; } inline void change_max(int &id, int u){ int s = query_sum(id, 1, MAX, u+1, MAX); if(!s) return; modify_itv(id, 1, MAX, u+1, MAX); modify_nod(id, 1, MAX, u, s); } inline void change_min(int &id, int u){ int s = query_sum(id, 1, MAX, 1, u-1); if(!s) return; modify_itv(id, 1, MAX, 1, u-1); modify_nod(id, 1, MAX, u, s); } }WST; int main(){ ios::sync_with_stdio(0), cout.tie(0), cout.tie(0); cin>>m; for(int i=1, opt, a, b, fa, fb; i<=m; ++i){ cin>>opt>>a; switch(opt){ case 1: num[++tot] = 1; WST.modify_nod(rt[tot], 1, MAX, a, 1); break; case 2: cin>>b; fa = find(a), fb = find(b); if(fa == fb) break; f[fb] = fa; rt[fa] = WST.merge(rt[fa], rt[fb], 1, MAX); num[fa] += num[fb]; break; case 3: cin>>b; WST.change_min(rt[find(a)], b); break; case 4: cin>>b; WST.change_max(rt[find(a)], b); break; case 5: cin>>b; fa = find(a); if(b > num[fa]){ cout<<"0\n"; break; } cout<<WST.query_rnk(rt[fa], 1, MAX, b)<<'\n'; break; case 6: cin>>b; fa = find(a), fb = find(b); if(fa == fb){ cout<<"0\n"; break; } cout<<(WST.mul[rt[fa]]>WST.mul[rt[fb]]?1:0)<<'\n'; break; default: cout<<num[find(a)]<<'\n'; } } return 0; }
本文作者:XiaoLe_MC
本文链接:https://www.cnblogs.com/xiaolemc/p/18270399
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步