线段树运用进阶

一、线段树分裂

类似于 \(FHQ-Treap\) 的方式,下给出模板题代码。

//Luogu5494
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e5+5,M=5e6+5;
int n,m,id,cnt,rt[N];
int sz[M],ls[M],rs[M];
void push_up(int x){
	sz[x]=sz[ls[x]]+sz[rs[x]];
}void add(int &x,int l,int r,int v,int k){
	if(!x) x=++id;
	if(l==r) return sz[x]+=k,void();
	int mid=(l+r)/2;
	if(v<=mid) add(ls[x],l,mid,v,k);
	else add(rs[x],mid+1,r,v,k);
	push_up(x);
}int kth(int x,int l,int r,int k){
	if(sz[x]<k) return -1;
	if(l==r) return l;
	int mid=(l+r)/2;
	if(sz[ls[x]]>=k) return kth(ls[x],l,mid,k);
	return kth(rs[x],mid+1,r,k-sz[ls[x]]);
}int merge(int x,int y,int l,int r){
	if(!x||!y) return x+y;
	if(l==r){
		sz[x]+=sz[y];
		return x;
	}int mid=(l+r)/2;
	ls[x]=merge(ls[x],ls[y],l,mid);
	rs[x]=merge(rs[x],rs[y],mid+1,r);
	return push_up(x),x;
}void spilt(int x,int &y,int k){
	if(!x) return;y=++id;
	if(sz[ls[x]]>=k) swap(rs[x],rs[y]);
	else spilt(rs[x],rs[y],k-sz[ls[x]]);
	if(sz[ls[x]]>k) spilt(ls[x],ls[y],k);
	push_up(x),push_up(y);
}int sum(int x,int l,int r,int L,int R){
	if(L<=l&&r<=R) return sz[x];
	int mid=(l+r)/2,re=0;
	if(L<=mid) re=sum(ls[x],l,mid,L,R);
	if(R>mid) re+=sum(rs[x],mid+1,r,L,R);
	return re;
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m,cnt=1;
	for(int i=1,x;i<=n;i++)
		cin>>x,add(rt[1],1,n,i,x);
	while(m--){
		int opt;cin>>opt;
		if(!opt){
			int p,x,y,a,lx,rx;
			cin>>p>>x>>y;
			lx=sum(rt[p],1,n,1,y);
			rx=sum(rt[p],1,n,x,y);
			spilt(rt[p],rt[++cnt],lx-rx);
			spilt(rt[cnt],a,rx);
			rt[p]=merge(rt[p],a,1,n);
		}if(opt==1){
			int p,t;cin>>p>>t;
			rt[p]=merge(rt[p],rt[t],1,n);
		}if(opt==2){
			int p,x,q;cin>>p>>x>>q;
			add(rt[p],1,n,q,x);
		}if(opt==3){
			int p,x,y;cin>>p>>x>>y;
			cout<<sum(rt[p],1,n,x,y)<<"\n";
		}if(opt==4){
			int p,k;cin>>p>>k;
			cout<<kth(rt[p],1,n,k)<<"\n";
		}
	}return 0;
}

二、线段树优化建图

考虑要从一段区间向另一段区间连边。容易想到首先建立中转点,将 \(n^2\) 的边数降到 \(n\)。当然,我们也可以结合线段树,将边数降到 \(\log n\) 级别。

下给出相对模板题的题的代码。

//CF786B
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=8e5+5;
struct node{int y,c;};
bool operator<(node x,node y){
	return x.c>y.c;
}vector<node>g[N];
int n,m,s,a[N],dis[N];
priority_queue<node>q;
void dij(int s){
	memset(dis,0x3f,sizeof(dis));
	q.push({s,dis[s]=0});
	while(q.size()){
		int x=q.top().y;
		int w=q.top().c;q.pop();
		if(w!=dis[x]) continue;
		for(auto nd:g[x]){
			int y=nd.y,c=nd.c;
			if(dis[y]>w+c)
				q.push({y,dis[y]=w+c});
		}
	}
}void build(int x,int l,int r){
	if(l==r){
		g[x+4*n].push_back({x,0});
		return a[l]=x,void();
	}int mid=(l+r)/2;
	g[x*2].push_back({x,0});
	g[x*2+1].push_back({x,0});
	g[x+4*n].push_back({x*2+4*n,0});
	g[x+4*n].push_back({x*2+1+4*n,0});
	build(x*2,l,mid);
	build(x*2+1,mid+1,r);
}void get1(int x,int l,int r,int L,int R,int gt,int w){
	if(L<=l&&r<=R)
		return g[gt].push_back({x+4*n,w}),void();
	int mid=(l+r)/2;
	if(L<=mid) get1(x*2,l,mid,L,R,gt,w);
	if(R>mid) get1(x*2+1,mid+1,r,L,R,gt,w);
}void get2(int x,int l,int r,int L,int R,int gt,int w){
	if(L<=l&&r<=R)
		return g[x].push_back({gt,w}),void();
	int mid=(l+r)/2;
	if(L<=mid) get2(x*2,l,mid,L,R,gt,w);
	if(R>mid) get2(x*2+1,mid+1,r,L,R,gt,w);
}signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>s,build(1,1,n);
	while(m--){
		int opt;cin>>opt;
		if(opt==1){
			int u,v,w;cin>>u>>v>>w;
			g[a[u]].push_back({a[v]+4*n,w});
		}if(opt==2){
			int u,l,r,w;cin>>u>>l>>r>>w;
			get1(1,1,n,l,r,a[u],w);
		}if(opt==3){
			int u,l,r,w;cin>>u>>l>>r>>w;
			get2(1,1,n,l,r,a[u]+4*n,w);
		}
	}dij(a[s]);
	for(int i=1;i<=n;i++)
		cout<<(dis[a[i]]>1e18?-1:dis[a[i]])<<" ";
	return 0;
}

三、线段树分治

主要特征是在一段时间内加边,将时间轴线段树化,然后进行区间修改,之后离线简单处理。通常要结合可撤销并查集,通常时间复杂度为 \(O(n\log^2n)\)

//BZOJ4045+Luogu5494
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,m,k,fa[N],sz[N];
struct mer{
	int sz,x,y;
};stack<mer>st;
struct ed{
	int x,y;
};vector<ed>g[4*N];
inline void init(){
	for(int i=1;i<=2*n;i++)
		fa[i]=i,sz[i]=1;
}inline int find(int x){
	return fa[x]==x?x:find(fa[x]);
}inline void unite(int x,int y){
	x=find(x),y=find(y);
	if(x==y) return;
	if(sz[x]<sz[y]) swap(x,y);
	st.push({sz[x],x,y});
	fa[y]=x,sz[x]+=sz[y];
}inline void chg(int x,int l,int r,int L,int R,ed e){
	if(L>R) return;
	if(L<=l&&r<=R){
		g[x].push_back(e);
		return;
	}int mid=(l+r)/2;
	if(L<=mid) chg(x*2,l,mid,L,R,e);
	if(R>mid) chg(x*2+1,mid+1,r,L,R,e);
}inline void solve(int x,int l,int r){
	int ans=1,ltp=st.size();
	for(auto e:g[x]){
		if(find(e.x)==find(e.y)){ans=0;break;}
		unite(e.x,e.y+n),unite(e.x+n,e.y);
	}int mid=(l+r)/2;
	if(!ans) for(int i=l;i<=r;i++) cout<<"No\n";
	else if(l==r) cout<<"Yes\n";
	else solve(x*2,l,mid),solve(x*2+1,mid+1,r);
	while(st.size()>ltp){
		mer x=st.top();st.pop();
		fa[x.y]=x.y,sz[x.x]=x.sz;
	}
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>k,init();
	while(m--){
		int x,y,l,r;
		cin>>x>>y>>l>>r;
		chg(1,1,k,l+1,r,{x,y});
	}solve(1,1,k);
	return 0;
}

未完待续……

posted @ 2024-11-29 13:26  长安一片月_22  阅读(1)  评论(0编辑  收藏  举报