【洛谷P1168】中位數(Splay)/(主席樹)
阿新 • • 發佈:2018-01-30
一個 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)/(主席樹)