方法 :线段树+treap的模板题
题解 :
首先是对于整个树的定义,其实treap部分并没有什么区别,只不过是单root改变为多root而已。
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <algorithm>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define INF 0x3f3f3f3f
#define N 200100
#define M 3000100
#define K 50100
using namespace std ;
struct node
{
int l,r,w,v,size,rnd;
}tr[M] ;
int n , m , size , ans;
int a[K] ;
int root[N] ;
然而这是treap的建图部分,build部分其实是线段树内的,原来的build内读值改变为insert即可。del操作其实就是把原来的点删掉,再加新的点即可。
// treap
void make_new(int k)
{
tr [k].size = tr [tr [k].l].size + tr [tr [k].r].size + tr [k].w ;
}
void lturn(int &k)
{
int t = tr [k].r ;
tr [k].r = tr [t].l ;
tr [t].l = k ;
tr [t].size = tr [k].size ;
make_new(k) ;
k = t ;
}
void rturn(int &k)
{
int t = tr [k].l ;
tr [k].l = tr [t].r ;
tr [t].r = k ;
tr [t].size = tr [k].size ;
make_new(k) ;
k = t ;
}
void insert(int &k , int x )
{
if (!k)
{
k = ++size ;
tr [k].size = tr [k].w = 1 ;
tr [k].v = x ;
tr [k].rnd = rand () ;
return ;
}
tr [k].size ++ ;
if (tr [k].v==x ){tr [k].w++;return ;}
if (x <tr [k].v){insert(tr [k].l,x );if (tr [tr [k].l].rnd<tr [k].rnd){rturn(k);}}
else {insert(tr [k].r,x );if (tr [tr [k].r].rnd<tr [k].rnd){lturn(k);}}
}
void del(int &k , int x )
{
if (tr [k].v==x )
{
if (tr [k].w>1 ){tr [k].w--;tr [k].size--;return ;}
if (tr [k].l*tr [k].r==0 )k=tr [k].l+tr [k].r;
else if (tr [tr [k].l].rnd<tr [tr [k].r].rnd){rturn(k);del(k,x );}
else {lturn(k);del(k,x );}
}
else if (x <tr [k].v){del(tr [k].l,x );tr [k].size--;}
else {del(tr [k].r,x );tr [k].size--;}
}
void build(int k,int l,int r,int x ,int num)
{
insert(root[k],num) ;
if (l==r)return ;
int mid = (l+r)>>1 ;
if (x <=mid)build(k<<1 ,l,mid,x ,num);
else build(k<<1 |1 ,mid+1 ,r,x ,num);
}
求第k的排名时,treap的rank查询没有区别,然而线段树部分的查询两种情况
1.如果是整区间直接返回treap的查询;
2.不整区间分段来搞,
而查询排名为k的值的时候,采用二分来搞
// 查询在区间排名。
void tr_rk(int k , int x )
{
if (!k) return ;
if (tr [k].v==x ){ans+=tr [tr [k].l].size;return ;}
else if (x <tr [k].v)tr_rk(tr [k].l,x );
else {ans+=tr [tr [k].l].size+tr [k].w;tr_rk(tr [k].r,x );}
}
void seg_rk(int k,int l,int r,int L,int R,int x )
{
if (L==l&&R==r){tr_rk(root[k],x );return ;}
int mid=(l+r)>>1 ;
if (mid>=R)seg_rk(k<<1 ,l,mid,L,R,x );
else if (mid<L)seg_rk(k<<1 |1 ,mid+1 ,r,L,R,x ) ;
else
{
seg_rk(k<<1 ,l,mid,L,mid,x );
seg_rk(k<<1 |1 ,mid+1 ,r,mid+1 ,R,x );
}
}
void query_val(int L,int R,int k)
{
int l=0 ,r=INF,tmp;
while (l<=r)
{
int mid=(l+r)>>1 ;
ans=1 ,seg_rk(1 ,1 ,n,L,R,mid);
if (ans<=k){l=mid+1 ,tmp=mid;}
else r=mid-1 ;
}
printf ("%d \n" , tmp);
}
前驱后继跟k的排名类似。
void tr_pre(int k,int num)
{
if (!k)return ;
if (tr[k].v<num){ans=max (ans,tr[k].v);tr_pre(tr[k].r,num);}
else tr_pre(tr[k].l,num);
}
void tr_sub(int k,int num)
{
if (!k)return ;
if (tr[k].v>num){ans=min (ans,tr[k].v);tr_sub(tr[k].l,num);}
else tr_sub(tr[k].r,num);
}
void query_pre(int k,int l,int r,int L,int R,int x)
{
if (l==L&&r==R){tr_pre(root[k],x);return ;}
int mid=(l+r)>>1 ;
if (mid>=R)query_pre(k<<1 ,l,mid,L,R,x);
else if (mid<L)query_pre(k<<1 |1 ,mid+1 ,r,L,R,x);
else
{
query_pre(k<<1 ,l,mid,L,mid,x);
query_pre(k<<1 |1 ,mid+1 ,r,mid+1 ,R,x);
}
}
void query_sub(int k,int l,int r,int L,int R,int x)
{
if (l==L&&r==R){tr_sub(root[k],x);return ;}
int mid=(l+r)>>1 ;
if (mid>=R)query_sub(k<<1 ,l,mid,L,R,x);
else if (mid<L)query_sub(k<<1 |1 ,mid+1 ,r,L,R,x);
else
{
query_sub(k<<1 ,l,mid,L,mid,x);
query_sub(k<<1 |1 ,mid+1 ,r,mid+1 ,R,x);
}
}
主函数for循环建树
int main()
{
scanf("%d %d " ,&n,&m );
for (int i=1 ;i<=n;i++)scanf("%d " ,&a[i]);
for (int i=1 ;i<=n;i++)build(1 ,1 ,n,i,a[i]);
for (int i=1 ;i<=m ;i++)
{
int jd;
scanf("%d " ,&jd);
int x ,y ,k;
switch(jd)
{
case 1 :scanf("%d %d %d " ,&x ,&y ,&k);ans=1 ;seg_rk(1 ,1 ,n,x ,y ,k);printf ("%d \n" ,ans);break ;
case 2 :scanf("%d %d %d " ,&x ,&y ,&k);query_val(x ,y ,k);break ;
case 3 :scanf("%d %d " ,&x ,&y );update(1 ,1 ,n,x ,y ,a[x ]);a[x ]=y ;break ;
case 4 :scanf("%d %d %d " ,&x ,&y ,&k);ans=0 ;query_pre(1 ,1 ,n,x ,y ,k);printf ("%d \n" ,ans);break ;
case 5 :scanf("%d %d %d " ,&x ,&y ,&k);ans=INF;query_sub(1 ,1 ,n,x ,y ,k);printf ("%d \n" ,ans);break ;
}
}
return 0 ;
}
至于空间计算,也不是很清楚,大概应该是4*n*lg(4n)+m这样的treap区间。
而线段树依然为4*n.