线段树分治
概述
配合可撤销的数据结构,用于离线维护图的连通性。
常见应用场景:一张图的边在一段时间里存在(加边删边),一些询问,不强制在线。
思想
主要利用线段树的分治性,让复杂度保证在了
通过线段树(一般不动态开点)的结构,对时间进行分治,从而在
考虑如何维护一个操作影响的时间区间。假设一个操作影响的时间是
这样的话,叶子节点到根节点上所有操作的效果之和就是该叶子节点所代表的时刻对应的局面。
那么对于一个询问,我们只要在线段树上找这个询问所在时间点,把根到叶子路径上的所有影响计算一遍,就能得出这个询问的答案。
但每次询问单独计算好像复杂度不太行,那就优化一下:把询问也插入到线段树里。每次递归整棵线段树,每到一个节点就更新这个节点上的操作,回溯时撤销,这样复杂度就是
例题
P5787 二分图 /【模板】线段树分治
思路
模板题,把每条边对应的时间段拍到线段树对应的节点上,最后一遍
判断是否为二分图的话使用扩展域并查集,每个点拆成两个点
并查集用按秩合并(高度小的并查集树连到高度大的上)优化,单次合并复杂度为
Code
点击查看代码
#include <bits/stdc++.h> using namespace std; #define ll long long #define ull unsigned long long #define l(x) (x<<1) #define r(x) (x<<1|1) #define mpr make_pair const int SIZE = 200005; const int mod = 998244353; int n, m, K; int ans[SIZE]; int fa[SIZE<<1], h[SIZE]; pair<int, pair<int, int> > st[SIZE]; int tot; inline int rd(){ int x = 0, f = 1; char ch = getchar(); while(ch < '0' || ch > '9'){ if(ch == '-') f = -1; ch = getchar(); } while(ch >= '0' && ch <= '9'){ x = (x<<1) + (x<<3) + (ch^48); ch = getchar(); } return x*f; } struct E{ int x, y, l, r; }edge[SIZE]; struct Tree{ int l, r; vector<int> e; }t[SIZE<<2]; void Build(int p, int l, int r){ t[p].l = l, t[p].r = r; if(l == r) return; int mid = (l + r) >> 1; Build(l(p), l, mid); Build(r(p), mid+1, r); } void add(int p, int l, int r, int val){ if(l <= t[p].l && r >= t[p].r){ t[p].e.push_back(val); return; } int mid = (t[p].l + t[p].r) >> 1; if(l <= mid) add(l(p), l, r, val); if(r > mid) add(r(p), l, r, val); } int get(int x){ if(x == fa[x]) return x; return get(fa[x]); } void upd(int p, bool &ff){ for(int i = 0; i < t[p].e.size(); i++){ int id = t[p].e[i]; int x = edge[id].x, y = edge[id].y; if(get(x) == get(y)) ff = 0; else{ int xx = get(x), yy = get(y), xx1 = get(x+n), yy1 = get(y+n); if(h[xx] > h[yy1]) swap(xx, yy1); st[++tot] = mpr(xx, mpr(yy1, h[yy1])); fa[xx] = yy1; h[yy1] += (h[yy1]==h[xx]); if(h[yy] > h[xx1]) swap(yy, xx1); st[++tot] = mpr(yy, mpr(xx1, h[xx1])); fa[yy] = xx1; h[xx1] += (h[xx1]==h[yy]); } } } void del(int las){ while(tot > las){ int x = st[tot].first, y = st[tot].second.first, val = st[tot].second.second; fa[x] = x; h[y] = val; tot--; } } void Solve(int p, bool ff){ bool jl = ff; int las = tot; upd(p, ff); if(t[p].l == t[p].r || !ff){ ans[t[p].l] = ff; del(las); ff = jl; return; } Solve(l(p), ff); Solve(r(p), ff); del(las); ff = jl; } int main(){ n = rd(), m = rd(), K = rd(); Build(1, 1, K); for(int i = 1; i <= m; i++){ edge[i].x = rd(), edge[i].y = rd(), edge[i].l = rd()+1, edge[i].r = rd(); if(edge[i].l > edge[i].r) continue; add(1, edge[i].l, edge[i].r, i); } for(int i = 1; i <= n*2; i++) fa[i] = i, h[i] = 1; Solve(1, 1); for(int i = 1; i <= K; i++){ if(ans[i]) printf("Yes\n"); else printf("No\n"); } return 0; }
本文作者:Semorius
本文链接:https://www.cnblogs.com/Semorius/p/17536701.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步