1. 程式人生 > >【洛谷P1168】中位數(Splay)/(主席樹)

【洛谷P1168】中位數(Splay)/(主席樹)

一個 void urn can oid 介紹 初始化 長度 while

Description

給出一個長度為N的非負整數序列A[i],對於所有1 ≤ k ≤ (N + 1) / 2,輸出A[1], A[2], …, A[2k - 1]的中位數。即前1,3,5,……個數的中位數。

N ≤ 100000

Solution

這題方法很多,這裏介紹splay的打法

求中位數即求第$(k+1)/$2小的數,用splay維護即可,只有2中操作:插入,旋轉

在樹上記錄一個\(c(u)\)表示節點\(u\)的子樹有幾個節點,用來判斷第n小

只要在插入和旋轉的時候維護就行了

Code

#include <cstdio>
#include <algorithm>
#define lc(x) T[(x)][0]
#define N 100010 int n,tot,k[N],T[N][2],s[N],rt,fa[N]; void rotate(int p){ int q=fa[p],y=fa[q],x=(T[q][1]==p); T[q][x]=T[p][x^1];fa[T[q][x]]=q; T[p][x^1]=q;fa[q]=p; fa[p]=y; if(y){ if(T[y][0]==q) T[y][0]=p; else if(T[y][1]==q) T[y][1]=p; } s[p]=s[q]; s[q]=s[T[q][0
]]+s[T[q][1]]+1;//這裏維護c(u) } void splay(int x){ for(int y;y=fa[x];rotate(x)) if(fa[y]) rotate((x==lc(y))==(y==lc(fa[y]))?y:x); rt=x; } void Insert(int x,int v){ if(!rt){ rt=++tot; s[rt]=1; k[rt]=v; return; } int y; while(y){ y=T[x][k[x]<v]; if
(!y){ y=++tot; k[y]=v; T[y][0]=T[y][1]=0; fa[y]=x; s[x]++;s[tot]++;//c(u)初始化 T[x][k[x]<v]=y; break; } x=y; } splay(y); } int Find(int x){ int r=0; for(int u=rt;;){ if(r+s[T[u][0]]+1==x) return k[u]; if(r+s[T[u][0]]+1<x) r+=s[T[u][0]]+1,u=T[u][1]; else u=T[u][0]; } } int main(){ scanf("%d",&n); for(int i=1;i<=n;++i){ int t; scanf("%d",&t); Insert(rt,t); if(i&1) printf("%d\n",Find((i>>1)+1)); } return 0; }

【洛谷P1168】中位數(Splay)/(主席樹)