题意:链接
方法:线段树+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 ;
//define
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的排名类似。
// treap的前驱后继
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.
时间: 2024-10-06 05:54:26