首页 > 其他分享 >线段树运用进阶

线段树运用进阶

时间:2024-11-29 13:34:41浏览次数:5  
标签:rt sz 进阶 int 线段 cin mid ls 运用

一、线段树分裂

类似于 \(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;
}

未完待续……

标签:rt,sz,进阶,int,线段,cin,mid,ls,运用
From: https://www.cnblogs.com/chang-an-22-lyh/p/18576479/segtree_plus-zj

相关文章

  • C语言进阶7:程序环境与预处理
    本章重点程序的翻译环境程序的执行环境详解:C语言程序的编译+链接预定义符号介绍预处理指令#define宏和函数的对比预处理操作符#和##的介绍命令定义预处理指令#include预处理指令#undef条件编译1.程序的翻译环境和执行环境在ANSIC的任何一种实现中,存在两个不同的环境......
  • React进阶面试题目(三)
    如何在React中实现滚动动画?在React中实现滚动动画可以通过多种方式实现,以下是一个基本的实现步骤:构建组件:首先构建需要展示滚动动画的组件,例如一个About组件,它包含一些文本或元素。监听滚动事件:在组件挂载后,通过window.onscroll事件监听滚动事件。更新状态:根据滚......
  • C语言——指针进阶
    1内存四区栈:先进后出堆:自由存储#include<stdio.h>#include<string.h>voidtest_one(){ 1.导致栈区内存溢出 intarray1[100000]; intarray2[100000]; intarray3[100000];//报错}voidtest_two(){ 2.字符串问题 charstr[]="sdfdsf"; char*......
  • WPF 桌面应用开发进阶:用户界面设计与交互逻辑的完美融合
    WindowsPresentationFoundation(WPF)是Microsoft推出的一个桌面应用开发框架,旨在提供一个高效、可扩展的用户界面设计工具。WPF支持数据绑定、模板化、响应式布局等先进的特性,能够帮助开发者快速构建现代化的桌面应用程序。本文将围绕WPF开发中的界面设计与交互逻辑,详细......
  • ThreeJs-03材质进阶
    一.uv贴图在3D计算机图形学中,UV映射是一种将2D纹理映射到3D模型表面的方法。在这里,“U”和“V”代表了2D纹理空间的坐标,这与2D笛卡尔坐标系统中的“X”和“Y”是类似的。在3D模型的每个顶点上,都会有一组对应的UV坐标,它们定义了3D模型在这个顶点上的表面应当对应纹理图像的哪个部......
  • 15Java集合进阶(异常、集合)
    一、异常1.1认识异常接下来,我们学习一下异常,学习异常有利于我们处理程序中可能出现的问题。我先带着同学们认识一下,什么是异常?我们阅读下面的代码,通过这段代码来认识异常。我们调用一个方法时,经常一部小心就出异常了,然后在控制台打印一些异常信息。其实打印的这些异常信息......
  • 16Java集合进阶(Set、Map集合、可变参数、斗地主案例)
    请先看我上篇文章15Java集合进阶(异常、集合)-CSDN博客一、Set系列集合1.1认识Set集合的特点Set集合是属于Collection体系下的另一个分支,它的特点如下图所示下面我们用代码简单演示一下,每一种Set集合的特点。//Set<Integer>set=newHashSet<>(); //无序、无索引、不重......
  • 洛谷题单指南-线段树-P1253 扶苏的问题
    原题链接:https://www.luogu.com.cn/problem/P1253题意解读:对于一个序列a[n],支持三种操作:1.将区间[l,r]所有数设置为x;2.将区间[l,r]所有数加上x;3.查询区间[l,r]的最大值解题思路:典型的线段树求解区间问题。线段树节点需要维护如下关键信息:1、区间l,r2、区间最大值v3、懒标记se......
  • 洛谷题单指南-线段树-P1438 无聊的数列
    原题链接:https://www.luogu.com.cn/problem/P1438题意解读:给定序列a[n],支持两种操作:1.给区间[l,r]每个数增加一个对应位置等差数列的元素,首项k,公差d;2.查询第x个元素值解题思路:直接用线段树求解。要实现区间修改,需要引入懒标记,而这里修改的值是要增加一个等差数列的某一项,需要保......
  • Vulkan进阶系列02 - GPU Rendering and Multi-Draw Indirect
    一:概述       GPU渲染与多重间接绘制(GPURenderingandMulti-DrawIndirect),本文介绍了一种通过将绘制调用生成和视锥剔除的处理过程转移到GPU上,从而有效减少CPU使用率的技术方案。二:DrawCall的生成        渲染大型场景的常见方法是遍历每个模型,并在每......