线段树分治
首先我们要理解线段树(现在指狭义的线段树)是什么。
线段树是一种容易维护区间的数据结构,是一种区间分治实体化的产物。
准确来说,比如你维护区间 [L,R],
其实就可以不断以中点分治下去。
由于每次分治区间长度都会除以 2 ,所以最多分治 log层,就形成了线段树。
那么线段树分治指什么呢?
实际上是一种维护时间区间的数据结构,同样是利用线段树的分治性,让复杂度保证在了 log级别。
但是,维护时间区间的东西还有很多,
比如 CDQ 分治,KD-Tree ,这一类数据结构都能维护时间区间,
那线段树分治的特殊作用在哪里呢?
实际上,一般而言,它的作用就是支持撤销操作,或者说就是维护了一个操作影响的时间区间.
例题:
题目大意
有一幅图,n个点,m条边,边有边权。
有三种操作:加边,删边,询问把图划分为两个点 集后两个端点属于同一个点集的边的最大值的最小值。
solution:
每次询问将当前存在的边排序后,从大到小依次加入,若加入一条边后出现奇环,则这条边就是答案
可以发现,那些加进去只产生偶环的边是没有用的。
因此只 有O(n)条边是有用的(即不产生环的边和第一次产生奇环的边)。
考虑线段树分治,把每条边按其存在时间的区间加入到线段树中。
对线段树每个节点,会 有许多条边。
预处理出每个节点上有用的O(n)条边。
然后每次询问就是把O(logn)个节点上的 信息合并。
令询问次数为Q。
由于可以将询问看作时间点建线段树,那么线段树中只有O(Q)个节点。
每个节点的信息可以由其父亲得来
#include<bits/stdc++.h> using namespace std; const int MAX_N=5+1e3; int n; struct DSF{ int fa[MAX_N],p[MAX_N]; void make_set(int x){ fa[x]=x,p[x]=0; } int find_set(int x){ //并查集维护奇偶性 if(fa[x]!=x){ int y=fa[x]; fa[x]=find_set(fa[x]); p[x]^=p[y]; } return fa[x]; } bool merge(int x,int y){ if(find_set(x)==find_set(y)) return p[x]^p[y]; int u=find_set(x),v=find_set(y); p[u]=p[x]^p[y]^1; fa[u]=v; return true; } bool member(int x,int y){ return find_set(x)==find_set(y); } }dsf; struct E{ int x,y,k; }; struct Q{ E x; int l,r; }; inline bool operator<(E a,E b){ return a.k>b.k; } vector<Q> e; struct SEG{ vector<E> tree[MAX_N<<2]; void build(int p,int l,int r){ tree[p].clear(); if(l==r) return; int mid=l+r>>1; build(p+p,l,mid); build(p+p+1,mid+1,r); } void change(int p,int l,int r,int x,int y,E key){ if(l==x&&r==y){ tree[p].push_back(key); return; } int mid=l+r>>1; if(y<=mid) return change(p+p,l,mid,x,y,key); else if(x>mid) return change(p+p+1,mid+1,r,x,y,key); else change(p+p,l,mid,x,mid,key),change(p+p+1,mid+1,r,mid+1,y,key); } void dfs(int p,int l,int r,vector<E> k,int ans){ for(int i=0;i<tree[p].size();++i) k.push_back(tree[p][i]); sort(k.begin(),k.end()); for(int i=1;i<=n;++i) dsf.make_set(i); for(int i=0;i<k.size();++i){ if(!dsf.member(k[i].x,k[i].y)){ dsf.merge(k[i].x,k[i].y); }else{ if(!dsf.merge(k[i].x,k[i].y)){ ans=max(ans,k[i].k); k.resize(i);//只保留[0,i-1] break; } } } if(l==r){ printf("%d\n",ans); return; } int mid=l+r>>1; dfs(p+p,l,mid,k,ans); dfs(p+p+1,mid+1,r,k,ans); } }seg; int main(){ int m,q; scanf("%d%d%d",&n,&m,&q); e.resize(m+1); for(int i=1;i<=m;++i){ scanf("%d%d%d",&e[i].x.x,&e[i].x.y,&e[i].x.k); e[i].l=1; e[i].r=-1; } vector<E> k; for(int i=1;i<=m;++i) k.push_back(e[i].x); sort(k.begin(),k.end()); for(int i=1;i<=n;++i) dsf.make_set(i); int top=1; for(int i=1;i<=q;++i){ char c=getchar(); while(c<'A'||c>'Z') c=getchar(); if(c=='D'){ int x; scanf("%d",&x); e[x].r=top-1; }else if(c=='A'){ e.push_back((Q){{0,0,0},0,0}); ++m; scanf("%d%d%d",&e[m].x.x,&e[m].x.y,&e[m].x.k); e[m].l=top; e[m].r=-1; }else{ ++top; } } for(int i=1;i<=m;++i) if(e[i].r==-1) e[i].r=top-1; --top; seg.build(1,1,top); for(int i=1;i<=m;++i) if(e[i].l<=e[i].r) seg.change(1,1,top,e[i].l,e[i].r,e[i].x); seg.dfs(1,1,top,vector<E>(),0); return 0; }