Maybe something will change|

Semorius

园龄:1年9个月粉丝:4关注:10

线段树分治

概述

配合可撤销的数据结构,用于离线维护图的连通性。

常见应用场景:一张图的边在一段时间里存在(加边删边),一些询问,不强制在线。

思想

主要利用线段树的分治性,让复杂度保证在了 log 级别。

通过线段树(一般不动态开点)的结构,对时间进行分治,从而在 O(nlogn) 的时间中维护出每一次询问的状态,相比 CDQ 分治只支持静态询问,线段树分治支持撤销。

考虑如何维护一个操作影响的时间区间。假设一个操作影响的时间是 [l,r],将其呈现在线段树上就会变成 log 段影响区间。

这样的话,叶子节点到根节点上所有操作的效果之和就是该叶子节点所代表的时刻对应的局面。

那么对于一个询问,我们只要在线段树上找这个询问所在时间点,把根到叶子路径上的所有影响计算一遍,就能得出这个询问的答案。

但每次询问单独计算好像复杂度不太行,那就优化一下:把询问也插入到线段树里。每次递归整棵线段树,每到一个节点就更新这个节点上的操作,回溯时撤销,这样复杂度就是 O(nlogn)

例题

P5787 二分图 /【模板】线段树分治

思路

模板题,把每条边对应的时间段拍到线段树对应的节点上,最后一遍 dfs 统计答案。

判断是否为二分图的话使用扩展域并查集,每个点拆成两个点 xx ,连边时 xy 连边,yx 连边。若 xx 在同一个连通块中,则该图不是二分图。(判断的话判当前边的两端点 xy 是否在同一个连通块中即可)

并查集用按秩合并(高度小的并查集树连到高度大的上)优化,单次合并复杂度为 O(logn) ,撤销复杂度为 O(1)

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 中国大陆许可协议进行许可。

posted @   Semorius  阅读(362)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起