序言
- 线段树是一种基于把查询区间划分为 l o g ( n ) log(n) log(n)个互相没有交集的小区间,并借助区间可加性维护的,所以只要把区间划分完成就可以独立查询修改
- 注意变量划分,避免重名
一,线段树套 multiset
- 注意是多重集
- 注意是lower还是upper
- 区间插入一个哨兵避免特判
- 建树时区间所有的点位置都要进入SET
- 线段树开4倍,多留余量
1 pos x
,将 p o s pos pos 位置的数修改为 x x x。
2 l r x
,查询整数 x x x 在区间 [ l , r ] [l,r] [l,r] 内的前驱(前驱定义为小于 x x x,且最大的数)。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <set>
using namespace std;
const int N = 2e5+100;
const int INF = 1e9;
struct node
{
int l,r;
multiset<int>s;
}tr[N];
int w[N];
void build (int u,int l,int r)
{
tr[u]={l,r};
tr[u].s.insert(INF);
tr[u].s.insert(-INF);
for (int i = l; i <= r; i ++ ) tr[u].s.insert(w[i]);
if(l==r) return ;
int mid = (l+r)>>1;
build (u<<1,l,mid);
build (u<<1|1,mid+1,r);
}
void modify(int u,int pos,int val)
{
auto j = tr[u].s.find(w[pos]);
tr[u].s.erase(j);
tr[u].s.insert(val);
if(tr[u].l==tr[u].r && tr[u].l==pos) return;
int mid = tr[u].l+tr[u].r>>1;
if(pos<=mid)modify(u<<1,pos,val);
else modify(u<<1|1,pos,val);
}
int query(int u,int a,int b,int x)
{
if(tr[u].l >= a && tr[u].r <= b)
{
auto it = tr[u].s.lower_bound(x);
it--;
return *it;
}
int mid = tr[u].l+tr[u].r >> 1;
int res = -INF;
if(a<=mid) res=max(res,query(u<<1,a,b,x));
if(b>mid) res=max(res,query(u<<1|1,a,b,x));
return res;
}
int main()
{
int n,m;
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> w[i];
build (1,1,n);
while (m -- )
{
int op,x,y,val;
scanf("%d%d%d",&op,&x,&y);
if(op==1) modify(1,x,y), w[x]=y;
else scanf("%d",&val),printf("%d\n",query(1,x,y,val));
}
return 0;
}
二,线段树套平衡树
第一行两个数 n , m n,m n,m,表示长度为 n n n 的有序序列和 m m m 个操作。
第二行有 n n n 个数,表示有序序列。
下面有 m m m 行, o p t opt opt 表示操作标号。
若 o p t = 1 opt=1 opt=1,则为操作 1 1 1,之后有三个数 l r k l~r~k l r k,表示查询 k k k 在区间 [ l , r ] [l,r] [l,r] 的排名。
若 o p t = 2 opt=2 opt=2,则为操作 2 2 2,之后有三个数 l r k l~r~k l r k,表示查询区间 [ l , r ] [l,r] [l,r] 内排名为 k k k 的数。
若 o p t = 3 opt=3 opt=3,则为操作 3 3 3,之后有两个数 p o s k pos~k pos k,表示将 p o s pos pos 位置的数修改为 k k k。
若 o p t = 4 opt=4 opt=4,则为操作 4 4 4,之后有三个数 l r k l~r~k l r k,表示查询区间 [ l , r ] [l,r] [l,r] 内 k k k 的前驱。
若 o p t = 5 opt=5 opt=5,则为操作 5 5 5,之后有三个数 l r k l~r~k l r k,表示查询区间 [ l , r ] [l,r] [l,r] 内 k k k 的后继。
- 插入哨兵之后查询小于
x
的数字数目时要-1
- 排名需要算自己
+1
- 二分查找时注意写法
- 注意修改传参的时候需要引用,查找大小和前驱时要使用正常传入
**注意:**巨大常数,需要O2优化
/// author: jixiang 2022.4.4
#include<bits/stdc++.h>
#define rep(i,j,k) for(int i = (j) ; i <= (k) ; i++)
#define fdow (i,j,k) for (int i = (j) ; i >= (k) ; i--)
#define debug puts("Debeg!!!!! wake up !!!")
#define Mset(a,v) memset(a,v,sizeof (a))
#define Mcpy(a,v) memcpy(a,v,sizeof (a))
#define mrep(i,j) for(int i=(h[j]);~i;i=ne[i])
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N = 2e6+9;
const int M = 2e6+9;
const int INF=2147483647;
int n,m;
struct node
{
int s[2],p,v;
int siz;
void init(int _v,int _p)
{
p = _p; v = _v;
siz = 1;
}
}tr[N];
int L[N],R[N],T[N],idx;
int w[N];
void up(int x)
{
tr[x].siz = tr[tr[x].s[1]].siz + tr[tr[x].s[0]].siz + 1;
}
void spin(int x)
{
int y = tr[x].p;
int z = tr[y].p;
int k = tr[y].s[1]==x;
tr[z].s[tr[z].s[1]==y] = x; tr[x].p =z;
tr[y].s[k]=tr[x].s[k^1]; tr[tr[x].s[k^1]].p=y;
tr[x].s[k^1]=y; tr[y].p=x;
up(y); up(x);
}
void splay (int &root,int x,int k)
{
while (tr[x].p!=k)
{
int y = tr[x].p;
int z = tr[y].p;
if(z!=k)
{
if((tr[y].s[1]==x) ^ (tr[z].s[1]==y))spin(x);
else spin(y);
}
spin(x);
}
if(k==0)root=x;
}
void insert(int &root,int v)
{
int u = root; int p =0;
while (u) p= u,u=tr[u].s[v>tr[u].v];
u=++idx;
if(p) tr[p].s[v>tr[p].v] = u;
tr[u].init(v,p);
splay(root,u,0);
}
int get_k(int root,int v)
{
int u =root;
int res= 0;
while (u)
{
if(tr[u].v<v) res+= tr[tr[u].s[0]].siz + 1 , u=tr[u].s[1];
else u=tr[u].s[0];
}
return res;
}
void update(int &root,int x,int y)
{
int u = root;
while (u)
{
if(tr[u].v == x) break;
if(tr[u].v < x) u=tr[u].s[1];
else u=tr[u].s[0];
}
splay(root,u,0);
/// 找到前驱和后继,然后删除后继左子树
int l,r;
l=tr[u].s[0];
r=tr[u].s[1];
while (tr[l].s[1]) l=tr[l].s[1];
while (tr[r].s[0]) r=tr[r].s[0];
splay (root,l,0);
splay(root,r,l);
tr[r].s[0] = 0;
up(r); up(l);
insert(root,y);
}
void build(int u,int a,int b)
{
L[u]=a; R[u]=b;
insert(T[u],INF); insert(T[u],-INF);
rep(i,a,b) insert(T[u],w[i]);
if(a==b)return ;
int mid = a+b >>1;
build(u<<1,a,mid);
build(u<<1|1,mid+1,b);
}
int query(int u,int a,int b,int x)
{
if(L[u]>=a && R[u]<=b) return get_k(T[u],x)-1;
int mid = L[u]+R[u] >>1;
int res = 0;
if(a<=mid) res+= query(u<<1,a,b,x);
if(b>mid) res+= query(u<<1|1,a,b,x);
return res;
}
void change (int u,int p,int x)
{
update(T[u],w[p],x);
if(L[u]==R[u]) return;
int mid = L[u]+R[u] >>1;
if(p<=mid)change(u<<1,p,x);
else change(u<<1|1,p,x);
}
int get_pre(int root,int v)
{
int u = root;
int res= -INF;
while (u)
{
if(tr[u].v<v) res=max(res,tr[u].v),u=tr[u].s[1];
else u=tr[u].s[0];
}
return res;
}
int get_suc(int root,int v)
{
int u = root;
int res= INF;
while (u)
{
if(tr[u].v > v) res= min(res,tr[u].v),u=tr[u].s[0];
else u=tr[u].s[1];
}
return res;
}
int query_pre(int u,int a,int b,int x)
{
if(L[u]>=a && R[u]<=b) return get_pre(T[u],x);
int mid = L[u]+R[u] >>1;
int res = -INF;
if(a<=mid) res=max(res,query_pre(u<<1,a,b,x));
if(b>mid) res=max(res,query_pre(u<<1|1,a,b,x));
return res;
}
int query_suc(int u,int a,int b,int x)
{
if(L[u]>=a && R[u]<=b) return get_suc(T[u],x);
int mid = L[u]+R[u] >>1;
int res = INF;
if(a<=mid) res=min(res,query_suc(u<<1,a,b,x));
if(b>mid) res=min(res,query_suc(u<<1|1,a,b,x));
return res;
}
int main()
{
read(n); read(m);
rep(i,1,n)read(w[i]);
build (1,1,n);
while(m--)
{
int op,a,b,k;
read(op);
if(op==1)
{
read(a); read(b); read(k);
wri(query(1,a,b,k)+1);puts("");
/// 注意排名选自己
}
if(op==2)
{
read(a); read(b); read(k);
int l = 0;
int r = 1e8;
while (l<r)
{
int mid = l+r+1 >>1;
if(query(1,a,b,mid)+1 <= k)l=mid;
else r=mid-1;
}
wri(l);puts("");
}
if(op==3)
{
read(a); read(k);
change(1,a,k);
w[a]=k;
}
if(op==4)
{
read(a); read(b); read(k);
wri(query_pre(1,a,b,k)); puts("");
}
if(op==5)
{
read(a); read(b); read(k);
wri(query_suc(1,a,b,k)); puts("");
}
}
return 0;
}