刷題總結——單旋(HNOI2017 bzoj4825)

題目:

Description

H 國是一個熱愛寫代碼的國家,那裏的人們很小去學校學習寫各類各樣的數據結構。伸展樹(splay)是一種數據
結構,由於代碼好寫,功能多,效率高,掌握這種數據結構成爲了 H 國的必修技能。有一天,邪惡的「卡」帶着
他的邪惡的「常數」來企圖毀滅 H 國。「卡」給 H 國的人洗腦說,splay 若是寫成單旋的,將會更快。「卡」稱
「單旋 splay」爲「spaly」。雖然說他說的很沒道理,但仍是有 H 國的人相信了,小 H 就是其中之一,spaly 馬
上成爲他的信仰。 而 H 國的國王,天然不容許這樣的風氣蔓延,國王構造了一組數據,數據由 m 個操做構成,
他知道這樣的數據確定打垮 spaly,可是國王還有不少不少其餘的事情要作,因此統計每一個操做所須要的實際代價
的任務就交給你啦。
 
數據中的操做分爲五種:
 
1. 插入操做:向當前非空 spaly 中插入一個關鍵碼爲 key 的新孤立節點。插入方法爲,先讓 key 和根比較,若是 
key 比根小,則往左子樹走,不然往右子樹走,如此反覆,直到某個時刻,key 比當前子樹根 x 小,而 x 的左子
樹爲空,那就讓 key 成爲 x 的左孩子; 或者 key 比當前子樹根 x 大,而 x 的右子樹爲空,那就讓 key 成爲 
x 的右孩子。該操做的代價爲:插入後,key 的深度。特別地,若樹爲空,則直接讓新節點成爲一個單個節點的樹
。(各節點關鍵碼互不相等。對於「深度」的解釋見末尾對 spaly 的描述)。
2. 單旋最小值:將 spaly 中關鍵碼最小的元素 xmin 單旋到根。操做代價爲:單旋前 xmin 的深度。
(對於單旋操做的解釋見末尾對 spaly 的描述)。
3. 單旋最大值:將 spaly 中關鍵碼最大的元素 xmax 單旋到根。操做代價爲:單旋前 xmax 的深度。
4. 單旋刪除最小值:先執行 2 號操做,而後把根刪除。因爲 2 號操做以後,根沒有左子樹,因此直接切斷根和右子
樹的聯繫便可(具體見樣例解釋)。 操做代價同 2 號操 做。
5. 單旋刪除最大值:先執行 3 號操做,而後把根刪除。 操做代價同 3 號操做。
 
對於不是 H 國的人,你可能須要瞭解一些 spaly 的知識,才能完成國王的任務:
 
a. spaly 是一棵二叉樹,知足對於任意一個節點 x,它若是有左孩子 lx,那麼 lx 的關鍵碼小於 x 的關鍵碼。
若是有右孩子 rx,那麼 rx 的關鍵碼大於 x 的關鍵碼。
b. 一個節點在 spaly 的深度定義爲:從根節點到該節點的路徑上一共有多少個節點(包括本身)。
c. 單旋操做是對於一棵樹上的節點 x 來講的。一開始,設 f 爲 x 在樹上的父親。若是 x 爲 f 的左孩子,那麼
執行 zig(x) 操做(如上圖中,左邊的樹通過 zig(x) 變爲了右邊的樹),不然執行 zag(x) 操做(在上圖中,將
右邊的樹通過 zag(f) 就變成了左邊的樹)。每當執 行一次 zig(x) 或者 zag(x),x 的深度減少 1,如此反覆,
直到 x 爲根。總之,單旋 x 就是經過反覆執行 zig 和 zag 將 x 變爲根。

Input

第一行單獨一個正整數 m。
接下來 m 行,每行描述一個操做:首先是一個操做編號 c∈[1,5],即問題描述中給出的五種操做中的編號,若 c
 = 1,則再輸入一個非負整數 key,表示新插入節點的關鍵碼。
1≤m≤10^5,1≤key≤10^9
全部出現的關鍵碼互不相同。任何一個非插入操做,必定保證樹非空。在未執行任何操做以前,樹爲空

Output

輸出共 m 行,每行一個整數,第 i 行對應第 i 個輸入的操做的代價。

Sample Input

5
1 2
1 1
1 3
4
5

Sample Output

1
2
2
2
2

題解:

首先是插入操做。容易發現,節點的深度是當前spaly中比它小中最大的、比它大的中最小的,兩個節點深度更大值+1。ios

接下來是旋轉&刪除。旋轉最小、大值的思路相似,這裏只討論最小值。畫圖能夠發現當前最小值右子樹的深度不變,本身深度變爲1,其他點深度+1。數據結構

把根刪除,就是其它節點深度所有-1.學習

那麼如今就要支持如下操做:在序列中間插入一個數、區間加減、單點修改、單點查詢、以及尋找第一個(或最後一個)比某值小的數。這題沒有強制在線,能夠用線段樹解決。若是在線能夠打splayspa

時間複雜度O(nlogn)code

至於如何維護就很麻煩了···線段樹的左右區間爲離散化後的鍵值,用tr,mx,mi分別表示鍵值區間內深度最小值,鍵值區間內鍵值最大的點的深度··鍵值區間內鍵值最小的點的深度,前一個用於求每一個點的再spaly中的father,後兩個用於求前驅後繼··(具體見代碼)blog

md好難打啊艹ip

代碼:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=100005;
int tr[N*4],mx[N*4],mi[N*4],tag[N*4],cnt[N*4],b[N],now,a[N],tot,m,n,op[N],deep;
inline int R()
{
  char c;int f=0;
  for(c=getchar();c<'0'||c>'9';c=getchar());
  for(;c<='9'&&c>='0';c=getchar())
    f=(f<<3)+(f<<1)+c-'0';
  return f;
}
inline void lsh()
{
  sort(b+1,b+n+1);
  for(int i=1;i<=n;i++)
    a[i]=lower_bound(b+1,b+n+1,a[i])-b;
}
inline void update(int k)
{
  if(cnt[k*2])  mi[k]=mi[k*2];else mi[k]=mi[k*2+1];
  if(cnt[k*2+1])  mx[k]=mx[k*2+1];else mx[k]=mx[k*2];
  tr[k]=min(tr[k*2],tr[k*2+1]);cnt[k]=cnt[k*2]+cnt[k*2+1];
}
inline void pushdown(int k)
{
  if(tag[k])
  {
    if(cnt[k*2])
      tag[k*2]+=tag[k],mi[k*2]+=tag[k],mx[k*2]+=tag[k],tr[k*2]+=tag[k];
    if(cnt[k*2+1])
      tag[k*2+1]+=tag[k],mi[k*2+1]+=tag[k],mx[k*2+1]+=tag[k],tr[k*2+1]+=tag[k];
    tag[k]=0;
  }
}
inline int pre(int k,int l,int r,int v)
{
  if(r==v)
    return mx[k];
  int mid=(l+r)/2;
  pushdown(k);
  if(v<=mid)  return pre(k*2,l,mid,v);
  int t=pre(k*2+1,mid+1,r,v);
  if(t)  return t;
  else return mx[k*2];
}
inline int nxt(int k,int l,int r,int v)
{
  if(l==v)
    return mi[k];
  int mid=(l+r)/2;
  pushdown(k);
  if(v>mid)  return nxt(k*2+1,mid+1,r,v);
  int t=nxt(k*2,l,mid,v);
  if(t)  return t;
  else return mi[k*2+1];
}
inline int getmin(int k,int l,int r)
{
  if(l==r)
  {
    deep=tr[k];return l;
  }
  pushdown(k);
  int mid=(l+r)/2;
  if(cnt[k*2])  return getmin(k*2,l,mid);
  else return getmin(k*2+1,mid+1,r);
}
inline int getmax(int k,int l,int r)
{
  if(l==r)
  {
    deep=tr[k];return l;
  }
  pushdown(k);
  int mid=(l+r)/2;
  if(cnt[k*2+1])  return getmax(k*2+1,mid+1,r);
  else return getmax(k*2,l,mid);
}
inline void insert(int k,int l,int r,int dep,int v)
{
  if(l==r)
  {
    tr[k]=mx[k]=mi[k]=dep;cnt[k]=1;return;
  }
  pushdown(k);
  int mid=(l+r)/2;
  if(v<=mid)  insert(k*2,l,mid,dep,v);
  else insert(k*2+1,mid+1,r,dep,v);
  update(k);
}
inline void Delete(int k,int l,int r,int v)
{
  if(l==r)
  {
    tr[k]=n,mx[k]=mi[k]=cnt[k]=0;return;
  }
  pushdown(k);
  int mid=(l+r)/2;
  if(v<=mid)  Delete(k*2,l,mid,v);
  else Delete(k*2+1,mid+1,r,v);
  update(k);
}
inline int find1(int k,int l,int r,int dep)
{
  if(l==r)  return l; 
  pushdown(k);
  int mid=(l+r)/2;
  if(cnt[k*2]&&tr[k*2]<dep)  return find1(k*2,l,mid,dep);
  return find1(k*2+1,mid+1,r,dep);
}
inline int find2(int k,int l,int r,int dep)
{
  if(l==r)  return l;
  pushdown(k);
  int mid=(l+r)/2;
  if(cnt[k*2+1]&&tr[k*2+1]<dep)  return find2(k*2+1,mid+1,r,dep);
  return find2(k*2,l,mid,dep);
}
inline void modify(int k,int l,int r,int x,int y,int v)
{
  if(!cnt[k])  return;
  if(l>=x&&r<=y)
  {
    tr[k]+=v,mx[k]+=v,mi[k]+=v,tag[k]+=v;
    return;
  }
  pushdown(k);
  int mid=(l+r)/2;
  if(x<=mid)  modify(k*2,l,mid,x,y,v);
  if(y>mid)  modify(k*2+1,mid+1,r,x,y,v);
  update(k);
}
int main()
{
  //freopen("a.in","r",stdin);
  m=R();
  for(int i=1;i<=m;i++)
  {
    op[i]=R();
    if(op[i]==1)  a[++n]=R(),b[n]=a[n];
  }
  lsh();n++;memset(tr,0x3f3f3f3f,sizeof(tr));
  for(int i=1;i<=m;i++)
  {
    if(op[i]==1)
    {
      now++;
      insert(1,0,n,deep=max(pre(1,0,n,a[now]),nxt(1,0,n,a[now]))+1,a[now]);
    }
    else if(op[i]==2||op[i]==4)
    {
      int q=getmin(1,0,n),p;
      Delete(1,0,n,q);p=find1(1,0,n,deep);modify(1,0,n,p,n,1);
      if(op[i]==2)  insert(1,0,n,1,q);else modify(1,0,n,0,n,-1);
    }
    else
    {
      int q=getmax(1,0,n),p; 
      Delete(1,0,n,q);p=find2(1,0,n,deep);modify(1,0,n,0,p,1);
      if(op[i]==3)  insert(1,0,n,1,q);else modify(1,0,n,0,n,-1);
    }
    printf("%d\n",deep);
  }
  return 0;
}
相關文章
相關標籤/搜索