1. 程式人生 > >BZOJ3196 & 洛谷3380:二逼平衡樹——題解

BZOJ3196 & 洛谷3380:二逼平衡樹——題解

org 標題 排名 color -s lan const amp 題解

https://www.lydsy.com/JudgeOnline/problem.php?id=3196

https://www.luogu.org/problemnew/show/P3380

(題面用洛谷的)

您需要寫一種數據結構(可參考題目標題),來維護一個有序數列,其中需要提供以下操作:

  1. 查詢k在區間內的排名

  2. 查詢區間內排名為k的值

  3. 修改某一位值上的數值

  4. 查詢k在區間內的前驅(前驅定義為嚴格小於x,且最大的數,若不存在輸出-2147483647)

  5. 查詢k在區間內的後繼(後繼定義為嚴格大於x,且最小的數,若不存在輸出2147483647)

參考:https://blog.csdn.net/clove_unique/article/details/51279573

其實看參考博客就行了,線段樹套splay極限卡時間。

簡單解釋一下第二種操作:

因為線段樹沒法查這個,所以一種直觀的想法是二分答案。

但由於周多麻煩的因素,我們存所有元素再排個序的效率並不高,於是我們直接對值域二分。

但是顯然有些值在這裏面是沒有的,所以要有一種很妙的判斷手段。

我們對於mid求出它在區間內之前有多少值,和k比較,如果比k小就不要。

這樣最後我們應該能夠找到比該元素小的數有k+1個且最小的元素,且並不難證明這個元素-1的值一定在樹中有。

(因為這個元素-1如果不存在的話就應該有k+1個比它小,和“最小的元素”矛盾。)

(人生第一棵樹套樹,感覺良好,卡常卡的質疑人生。)

#include<cstdio>
#include<algorithm>
using namespace std;
const int INF=2147483647;
const int N=4e6+5;
inline int read(){
    int X=0,w=0;char ch=0;
    while(ch<0||ch>9){w|=ch==-;ch=getchar();}
    while(ch>=0&&ch<=9)X=(X<<3)+(X<<1)+(ch^48
),ch=getchar(); return w?-X:X; } int s[N],maxn,rt[N],sz; int fa[N],tr[N][2],key[N],size[N],cnt[N]; inline bool get(int x){ return tr[fa[x]][1]==x; } inline void clear(int x){ fa[x]=tr[x][0]=tr[x][1]=key[x]=cnt[x]=size[x]=0; } inline void splay_upd(int x){ if(!x)return; size[x]=cnt[x]; if(tr[x][0])size[x]+=size[tr[x][0]]; if(tr[x][1])size[x]+=size[tr[x][1]]; } inline void rotate(int x){ int y=fa[x],z=fa[y],which=get(x); tr[y][which]=tr[x][which^1];fa[tr[y][which]]=y; fa[y]=x;tr[x][which^1]=y;fa[x]=z; if(z)tr[z][tr[z][1]==y]=x; splay_upd(y);splay_upd(x); } inline void splay(int i,int x){ int f=fa[x]; while(f){ if(fa[f])rotate(get(x)==get(f)?f:x); rotate(x);f=fa[x]; } rt[i]=x; } inline void splay_ins(int i,int v){ if(!rt[i]){ rt[i]=++sz; fa[sz]=tr[sz][0]=tr[sz][1]=0; size[sz]=cnt[sz]=1;key[sz]=v; return; } int now=rt[i],f=0; while(233){ if(key[now]==v){ cnt[now]++;splay_upd(f);splay(i,now); return; } f=now;now=tr[now][key[now]<v]; if(!now){ ++sz; fa[sz]=f;tr[sz][0]=tr[sz][1]=0; size[sz]=cnt[sz]=1;key[sz]=v; tr[f][key[f]<v]=sz; splay_upd(f);splay(i,sz); return; } } } inline int splay_find(int i,int v){//查詢比v小的數的個數 int ans=0,now=rt[i]; while(233){ if(!now)return ans; if(v<key[now])now=tr[now][0]; else{ ans+=(tr[now][0]?size[tr[now][0]]:0); if(v==key[now]){ splay(i,now); return ans; } ans+=cnt[now]; now=tr[now][1]; } } } inline int splay_pre(int i){ int now=tr[rt[i]][0]; while(tr[now][1])now=tr[now][1]; return now; } inline int splay_nxt(int i){ int now=tr[rt[i]][1]; while(tr[now][0])now=tr[now][0]; return now; } inline void splay_del(int i,int x){ splay_find(i,x); if(cnt[rt[i]]>1){ cnt[rt[i]]--;return; } if(!tr[rt[i]][0]&&!tr[rt[i]][1]){ clear(rt[i]);rt[i]=0;return; } if(!tr[rt[i]][0]){ int oldroot=rt[i];rt[i]=tr[rt[i]][1];fa[rt[i]]=0;clear(oldroot);return; } else if(!tr[rt[i]][1]){ int oldroot=rt[i];rt[i]=tr[rt[i]][0];fa[rt[i]]=0;clear(oldroot);return; } int leftbig=splay_pre(i),oldroot=rt[i]; splay(i,leftbig); fa[tr[oldroot][1]]=rt[i]; tr[rt[i]][1]=tr[oldroot][1]; clear(oldroot); splay_upd(rt[i]); } inline void seg_mdy(int a,int l,int r,int x,int v){ splay_del(a,s[x]);splay_ins(a,v); if(l==r)return; int mid=(l+r)>>1; if(x<=mid)seg_mdy(a<<1,l,mid,x,v); else seg_mdy(a<<1|1,mid+1,r,x,v); } inline int seg_find(int a,int l,int r,int l1,int r1,int v){ if(r<l1||r1<l)return 0; if(l1<=l&&r<=r1)return splay_find(a,v); int mid=(l+r)>>1; return seg_find(a<<1,l,mid,l1,r1,v)+seg_find(a<<1|1,mid+1,r,l1,r1,v); } inline int seg_pre(int a,int l,int r,int l1,int r1,int v){ if(r<l1||r1<l)return -INF; if(l1<=l&&r<=r1){ splay_ins(a,v); int tmp=splay_pre(a); splay_del(a,v); return !tmp?-INF:key[tmp]; } int mid=(l+r)>>1; return max(seg_pre(a<<1,l,mid,l1,r1,v),seg_pre(a<<1|1,mid+1,r,l1,r1,v)); } inline int seg_nxt(int a,int l,int r,int l1,int r1,int v){ if(r<l1||r1<l)return INF; if(l1<=l&&r<=r1){ splay_ins(a,v); int tmp=splay_nxt(a); splay_del(a,v); return !tmp?INF:key[tmp]; } int mid=(l+r)>>1; return min(seg_nxt(a<<1,l,mid,l1,r1,v),seg_nxt(a<<1|1,mid+1,r,l1,r1,v)); } inline void seg_build(int a,int l,int r){ for(int i=l;i<=r;i++)splay_ins(a,s[i]); if(l==r)return; int mid=(l+r)>>1; seg_build(a<<1,l,mid);seg_build(a<<1|1,mid+1,r); } int main(){ int n=read(),m=read(); for(int i=1;i<=n;++i){ s[i]=read(),maxn=max(maxn,s[i]); } seg_build(1,1,n); for(int i=1;i<=m;++i){ int op=read(); if(op==1){ int l=read(),r=read(),k=read(); printf("%d\n",seg_find(1,1,n,l,r,k)+1); } if(op==2){ int l=read(),r=read(),k=read(); int l1=0,r1=maxn+1; while(l1<r1){ int mid=(l1+r1)>>1; int rk=seg_find(1,1,n,l,r,mid); if(rk<k)l1=mid+1; else r1=mid; } printf("%d\n",l1-1); } if(op==3){ int pos=read(),k=read(); seg_mdy(1,1,n,pos,k); s[pos]=k;maxn=max(maxn,s[pos]); } if(op==4){ int l=read(),r=read(),k=read(); printf("%d\n",seg_pre(1,1,n,l,r,k)); } if(op==5){ int l=read(),r=read(),k=read(); printf("%d\n",seg_nxt(1,1,n,l,r,k)); } } return 0; }

+++++++++++++++++++++++++++++++++++++++++++

+本文作者:luyouqi233。               +

+歡迎訪問我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

BZOJ3196 & 洛谷3380:二逼平衡樹——題解