可持久化线段树 学习笔记
引入
嗯嗯因为我打了一次测试
所以学了这个可持久化线段树(
怎么说其实这东西我很久之前学过,只是有点忘了
深刻认识到了写博客的重要性啦!(>ω・* )ノ
这东西其实很简单,也加强了对动态开点线段树的理解
思想
一个线段树,如果单点修改,只会修改一条链上的值,那你就直接每次新建一条链,就可以维护这棵线段树的历史版本啦!
具体看这个图
例题
区间第
做法是建一个值域可持久化线段树,然后在上面二分
代码
主要是实现比较难
我直接贴代码,反正算法笔记是给自己看的ヾ(o・ω・)ノ
#include <bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 1e5+5, M = 1e9+5;
int n,m,a[N],b[N];
int rd()
{
int x,f=0; char ch;
while(!isdigit(ch=getchar())) if(ch=='-') f=1;
for(x=(ch^48); isdigit(ch=getchar()); x=(x<<1)+(x<<3)+(ch^48));
return f?-x:x;
}
struct Sagiri { int ls,rs,dat; } t[N<<5]; //动态开点要存左右儿子的编号
int rt[N],tot; //rt[i]表示 1~i 次修改后的线段树
void change(int &p, int q, int l, int r, int x, int v) //动态开点不需要 build,直接一个个 change 上去不香吗
{
t[p=++tot]=t[q]; //这行很重要,表示新的线段树要在原来线段树的基础上改,要继承原来的左右儿子和 dat
if(l==r) { t[p].dat+=v; return ; }
int mid=(l+r)>>1;
if(x<=mid) change(t[p].ls,t[q].ls,l,mid,x,v);
else if(r>mid) change(t[p].rs,t[q].rs,mid+1,r,x,v);
t[p].dat=t[t[p].ls].dat+t[t[p].rs].dat;
}
int query(int p, int q, int l, int r, int k) //可持久化线段树上二分
{
if(l==r) return l;
int mid=(l+r)>>1;
int lcnt=t[t[p].ls].dat-t[t[q].ls].dat; //表示的是左儿子的个数
if(k<=lcnt) return query(t[p].ls,t[q].ls,l,mid,k); //如果询问的 k<=lcnt, 就在左子树里搜
else return query(t[p].rs,t[q].rs,mid+1,r,k-lcnt); //否则就去右子树
}
int main()
{
n=rd(),m=rd();
for(int i=1; i<=n; i++) scanf("%d",&a[i]);
for(int i=1; i<=n; i++) change(rt[i],rt[i-1],-M,M,a[i],1);
int L,R,k;
while(m--)
{
L=rd(),R=rd(),k=rd();
printf("%d\n",query(rt[R],rt[L-1],-M,M,k));
}
return 0;
}
例题2
就是那次测试的 T4,
如果要维护区间前
代码
struct Sagiri { int ls,rs,cnt; ll dat; } t[N<<5];
//日常把纱雾老婆拿来命名线段树结构体 要维护区间的个数 cnt 和区间之和 dat
int rt[N];
void change(int &p, int q, ll l, ll r, int x)
{
t[p=++tot]=t[q];
if(l==r) { t[p].cnt++; t[p].dat+=l; return ; }
ll mid=(l+r)>>1;
if(x<=mid) change(t[p].ls,t[q].ls,l,mid,x);
else change(t[p].rs,t[q].rs,mid+1,r,x);
t[p].dat=t[t[p].ls].dat+t[t[p].rs].dat;
t[p].cnt=t[t[p].ls].cnt+t[t[p].rs].cnt;
}
ll query_sumk(int p, int q, ll l, ll r, int k)
{
if(l==r) return (ll)l*k;
ll mid=(l+r)>>1;
int rcnt=t[t[p].rs].cnt-t[t[q].rs].cnt;
if(k<=rcnt) return query_sumk(t[p].rs,t[q].rs,mid+1,r,k);
else return query_sumk(t[p].ls,t[q].ls,l,mid,k-rcnt)+t[t[p].rs].dat-t[t[q].rs].dat; //这边记得要减
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战