线段树专题

本文探讨了线段树和主席树两种数据结构在查找第k小元素、合并操作及单点修改场景中的应用,通过实例LuoguP4556展示了如何利用它们优化问题解决。同时,涵盖了主席树在查找最小元素中的角色和线段树的合并技巧。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

线段树

线段树合并

Luogu P4556

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MaxN=210000;
const int MaxS=110000;
template<typename Typename>
inline void Swap(Typename&x,Typename&y){
	Typename t=x;x=y;y=t;
}
struct SegmentTree{
	int Son[2][MaxN<<5],Data[MaxN<<5],Place[MaxN<<5],cnt;
	SegmentTree(){cnt=0;memset(Son,0,sizeof(Son));}
	inline void PushUp(int now){
		if(Data[Son[0][now]]>=Data[Son[1][now]]){
			Place[now]=Place[Son[0][now]];
			Data[now]=Data[Son[0][now]];
		}
		else{
			Place[now]=Place[Son[1][now]];
			Data[now]=Data[Son[1][now]];
		}
	}
	inline int Merge(int x,int y,int Left,int Right){
		if(x==0||y==0)return x+y;
		if(Left==Right){
			Data[x]+=Data[y];
			Place[x]=Left;
		}
		else{
			register int mid=(Left+Right)>>1;
			Son[0][x]=Merge(Son[0][x],Son[0][y],Left,mid);
			Son[1][x]=Merge(Son[1][x],Son[1][y],mid+1,Right);
			PushUp(x);
		}
		return x;
	}
	inline void Modify(int &now,int Left,int Right,int pos,int val){
		if(now==0)now=++cnt;
		if(Left==Right){
			Place[now]=Left;
			Data[now]+=val;
		}
		else{
			register int mid=(Left+Right)>>1;
			if(pos<=mid)Modify(Son[0][now],Left,mid,pos,val);
			else Modify(Son[1][now],mid+1,Right,pos,val);
			PushUp(now);
		}
	}
};SegmentTree Seg;

int To[MaxN*2],Next[MaxN*2],Last[MaxN],cnt;
inline void Insert(int from,int to){
	To[cnt]=to;
	Next[cnt]=Last[from];Last[from]=cnt++;
}

int Deep[MaxN],Dp[MaxN][31],Bit[31];
inline void Dfs(int from,int dad){
	Deep[from]=Deep[dad]+1;
	Dp[from][0]=dad;
	for(register int i=1;i<=30;i++)
		Dp[from][i]=Dp[Dp[from][i-1]][i-1];
	for(register int k=Last[from];k!=-1;k=Next[k])
		if(To[k]!=dad)Dfs(To[k],from);
}
inline int GetLca(int x,int y){
	if(Deep[x]<Deep[y])Swap(x,y);
	for(register int i=30;i>=0;i--)
		if(Deep[Dp[x][i]]>=Deep[y])x=Dp[x][i];
	if(x==y)return x;
	for(register int i=30;i>=0;i--)
		if(Dp[x][i]!=Dp[y][i])
			x=Dp[x][i],y=Dp[y][i];
	return Dp[x][0];
}

int Root[MaxN],Ans[MaxN];
inline void Solve(int from,int dad){
	for(register int k=Last[from];k!=-1;k=Next[k]){
		if(To[k]==dad)continue;Solve(To[k],from);
		Root[from]=Seg.Merge(Root[from],Root[To[k]],1,MaxS);
	}
	Ans[from]=Seg.Place[Root[from]];
	if(Seg.Data[Root[from]]==0)Ans[from]=0;
}
int n,m;
inline void Init(){
	Bit[0]=1;
	for(register int i=1;i<=n;i++)
		Bit[i]=Bit[i-1]<<1;
}
inline void ReInit(){
	cnt=0;
	memset(Last,-1,sizeof(Last));
}
inline void Work(){
	scanf("%d%d",&n,&m);
	for(register int i=1,x,y;i<n;i++){
		scanf("%d%d",&x,&y);
		Insert(x,y);
		Insert(y,x);
	}
	Dfs(1,0);
	for(register int i=1,x,y,d;i<=m;i++){
		scanf("%d%d%d",&x,&y,&d);
		int lca=GetLca(x,y);
		Seg.Modify(Root[lca],1,MaxS,d,-1);
		if(Dp[lca][0])Seg.Modify(Root[Dp[lca][0]],1,MaxS,d,-1);
		Seg.Modify(Root[x],1,MaxS,d,1);
		Seg.Modify(Root[y],1,MaxS,d,1);
	}
	Solve(1,0);
	for(register int i=1;i<=n;i++)
		printf("%d\n",Ans[i]);
}
int Task;
int main(){
	//scanf("%d",&Task);
	Task=1;Init();
	for(register int Case=1;Case<=Task;Case++){
		ReInit();
		Work();
	}
	return 0;
} 

主席树

寻找第 k k k

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

template<int MaxN>
struct SegmentTrees{
	struct Nodes{
		int Left,Right;
		int Son[2];
		int Data;
	};Nodes Node[MaxN*30];
	int cnt;
	inline void Update(int&now,int pre,int left,int right,int pos){
		now=++cnt;
		Node[now].Data=Node[pre].Data+1;
		Node[now].Son[0]=Node[pre].Son[0];
		Node[now].Son[1]=Node[pre].Son[1];
		if(left==right)return;
		else{
			int mid=(left+right)>>1;
			if(pos<=mid)
				Update(Node[now].Son[0],Node[pre].Son[0],left,mid,pos);
			else Update(Node[now].Son[1],Node[pre].Son[1],mid+1,right,pos);
		}
	}
	inline int Query(int x,int y,int left,int right,int rank){
		int t=Node[Node[y].Son[0]].Data-Node[Node[x].Son[0]].Data;
		if(left==right)return left;
		else{
			int mid=(left+right)>>1;
			if(rank<=t)
				return Query(Node[x].Son[0],Node[y].Son[0],left,mid,rank);
			else return Query(Node[x].Son[1],Node[y].Son[1],mid+1,right,rank-t);
		}
	}
};
const int MaxN=1e5+1e4;

SegmentTrees<MaxN>T;
int Root[MaxN];
int Arr[MaxN],Copy[MaxN];
int n,m,len;

int main(){
	n=Reader();m=Reader();
	for(int i=1;i<=n;i++){
		Arr[i]=Reader();
		Copy[i]=Arr[i];
	}
	
	sort(Copy+1,Copy+n+1);
	len=unique(Copy+1,Copy+n+1)-Copy-1;
	for(int i=1;i<=n;i++)
		Arr[i]=lower_bound(Copy+1,Copy+len+1,Arr[i])-Copy;
	
	for(int i=1;i<=n;i++){
		T.Update(Root[i],Root[i-1],1,len,Arr[i]);
	}
	for(int i=1,l,r,c;i<=m;i++){
		l=Reader();r=Reader();c=Reader();
		printf("%d\n",Copy[T.Query(Root[l-1],Root[r],1,len,c)]);
	}
	
	return 0;
}

单点修改寻找第 k k k

#include<cstdio>
#include<cstring> 
#include<algorithm>
using namespace std;

const int MaxN=5e4+1e3;

int Son[2][MaxN*200],Arr[MaxN],limit;
int Data[MaxN*200],Address[MaxN*2],Root[MaxN],size,cnt,Tmp[2][20];

inline int LowBit(const int&x){
	return x&(-x);
}
inline void Modify(int&now,int left,int right,int pos,int val){
	if(!now)now=++cnt;
	Data[now]+=val;
	if(left==right)return;
	else{
		int mid=(left+right)>>1;
		if(pos<=mid)Modify(Son[0][now],left,mid,pos,val);
		else Modify(Son[1][now],mid+1,right,pos,val);
	}
}
inline void Modify(int pos,int val){
	int rank=lower_bound(Address+1,Address+size+1,Arr[pos])-Address;
	for(int i=pos;i<=limit;i+=LowBit(i))Modify(Root[i],1,size,rank,val);
}
inline int Query(int left,int right,int rank){
	if(left==right)return left;
	int mid=(left+right)>>1,sum=0;
	for(int i=1;i<=Tmp[1][0];i++)sum+=Data[Son[0][Tmp[1][i]]];
	for(int i=1;i<=Tmp[0][0];i++)sum-=Data[Son[0][Tmp[0][i]]];
	if(rank<=sum){
		for(int i=1;i<=Tmp[1][0];i++)Tmp[1][i]=Son[0][Tmp[1][i]];
		for(int i=1;i<=Tmp[0][0];i++)Tmp[0][i]=Son[0][Tmp[0][i]];
		return Query(left,mid,rank);
	}
	else{
		for(int i=1;i<=Tmp[1][0];i++)Tmp[1][i]=Son[1][Tmp[1][i]];
		for(int i=1;i<=Tmp[0][0];i++)Tmp[0][i]=Son[1][Tmp[0][i]];
		return Query(mid+1,right,rank-sum);
	}
}
inline int Ask(int begin,int end,int rank){
	Tmp[1][0]=Tmp[0][0]=0;
	for(int i=end;i>=1;i-=LowBit(i))Tmp[1][++Tmp[1][0]]=Root[i];
	for(int i=begin-1;i>=1;i-=LowBit(i))Tmp[0][++Tmp[0][0]]=Root[i];
	return Query(1,size,rank);
}

struct Operations{
	int l,r,k;
	bool opt;
};Operations Qry[11000];
char St[10];

int n,m;
int main(){
	scanf("%d%d",&n,&m);limit=n;
	for(int i=1;i<=n;i++)
		scanf("%d",&Arr[i]),Address[++size]=Arr[i];
	for(int i=1;i<=m;i++){
		scanf("%s",St+1);
		Qry[i].opt=(St[1]=='Q');
		if(Qry[i].opt){
			scanf("%d%d%d",&Qry[i].l,&Qry[i].r,&Qry[i].k);
		}
		else{
			scanf("%d%d",&Qry[i].l,&Qry[i].k);
			Address[++size]=Qry[i].k;
		}
	}
	
	sort(Address+1,Address+1+size);
	size=unique(Address+1,Address+1+size)-Address-1;
	
	for(int i=1;i<=n;i++)
		Modify(i,1);
	for(int i=1;i<=m;i++){
		if(Qry[i].opt){
			printf("%d\n",Address[Ask(Qry[i].l,Qry[i].r,Qry[i].k)]);
		}
		else{
			Modify(Qry[i].l,-1);
			Arr[Qry[i].l]=Qry[i].k;
			Modify(Qry[i].l,1);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值