1208. [HNOI2004]寵物收養場【平衡樹-splay】
Description
最近,阿Q開了一間寵物收養所。收養所提供兩種服務:收養被主人遺棄的寵物和讓新的主人領養這些寵物。每個領養者都希望領養到自己滿意的寵物,阿Q根據領養者的要求通過他自己發明的一個特殊的公式,得出該領養者希望領養的寵物的特點值a(a是一個正整數,a<2^31),而他也給每個處在收養所的寵物一個特點值。這樣他就能夠很方便的處理整個領養寵物的過程了,寵物收養所總是會有兩種情況發生:被遺棄的寵物過多或者是想要收養寵物的人太多,而寵物太少。 1. 被遺棄的寵物過多時,假若到來一個領養者,這個領養者希望領養的寵物的特點值為a,那麽它將會領養一只目前未被領養的寵物中特點值最接近a的一只寵物。(任何兩只寵物的特點值都不可能是相同的,任何兩個領養者的希望領養寵物的特點值也不可能是一樣的)如果有兩只滿足要求的寵物,即存在兩只寵物他們的特點值分別為a-b和a+b,那麽領養者將會領養特點值為a-b的那只寵物。 2. 收養寵物的人過多,假若到來一只被收養的寵物,那麽哪個領養者能夠領養它呢?能夠領養它的領養者,是那個希望被領養寵物的特點值最接近該寵物特點值的領養者,如果該寵物的特點值為a,存在兩個領養者他們希望領養寵物的特點值分別為a-b和a+b,那麽特點值為a-b的那個領養者將成功領養該寵物。 一個領養者領養了一個特點值為a的寵物,而它本身希望領養的寵物的特點值為b,那麽這個領養者的不滿意程度為abs(a-b)。【任務描述】你得到了一年當中,領養者和被收養寵物到來收養所的情況,希望你計算所有收養了寵物的領養者的不滿意程度的總和。這一年初始時,收養所裏面既沒有寵物,也沒有領養者。
Input
第一行為一個正整數n,n<=80000,表示一年當中來到收養所的寵物和領養者的總數。接下來的n行,按到來時間的先後順序描述了一年當中來到收養所的寵物和領養者的情況。每行有兩個正整數a, b,其中a=0表示寵物,a=1表示領養者,b表示寵物的特點值或是領養者希望領養寵物的特點值。(同一時間呆在收養所中的,要麽全是寵物,要麽全是領養者,這些寵物和領養者的個數不會超過10000個)
Output
僅有一個正整數,表示一年當中所有收養了寵物的領養者的不滿意程度的總和mod 1000000以後的結果。
Sample Input
50 2
0 4
1 3
1 2
1 5
Sample Output
3
(abs(3-2) + abs(2-4)=3,最後一個領養者沒有寵物可以領養)
令人智熄……寫了好久發現,敲板子敲得漏洞百出
改完板子後發現自己又讀錯題了……GG
裸的splay+一點小處理
判斷當前是寵物多還是人多,如果人多的時候再來人就把人插入樹中
再來寵物就從樹中取一個出來。
當寵物多時同理
我即使被釘在棺材裏,也要用腐朽的聲音喊出:
辣雞數據結構毀我青春!
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #define MAXN (100000+10) #define MOD (1000000) using namespace std; int Father[MAXN]; int Son[MAXN][2]; int Key[MAXN];//結點代表的數字 int Cnt[MAXN];//結點代表數字出現次數 int Size[MAXN];//子樹大小(含自身) int Root,sz; int INF; void Clear(int x)//清空當前節點的數值 { Son[x][0]=Son[x][1]=Key[x]=Cnt[x]=Size[x]=Father[x]=0; } int Get(int x)//0為左1為右 { return (Son[Father[x]][1]==x); } void Update(int x)//更新當前點的size值(用於發生修改之後) { if (x) { Size[x]=Cnt[x]; if (Son[x][0]) Size[x]+=Size[Son[x][0]]; if (Son[x][1]) Size[x]+=Size[Son[x][1]]; } } void Rotate(int x) { int Fa=Father[x]; int Fafa=Father[Fa]; int wh=Get(x); Son[Fa][wh]=Son[x][wh^1]; if (Son[Fa][wh]) Father[Son[Fa][wh]]=Fa; Son[x][wh^1]=Fa; Father[Fa]=x; if (Fafa) Son[Fafa][Son[Fafa][1]==Fa]=x; Father[x]=Fafa; Update(Fa); Update(x); } void Splay(int x) { for (int Fa; Fa=Father[x]; Rotate(x)) if (Father[Fa]) Rotate(Get(x)==Get(Fa)?Fa:x); Root=x; } void Insert(int x) { if (Root==0) { sz++; Key[sz]=x; Cnt[sz]=Size[sz]=1; Root=sz; return; } int now=Root,Fa=0; while (1) { if (Key[now]==x) { Cnt[now]++; Update(now); Splay(now); break; } Fa=now; now=Son[now][x>Key[now]]; if (now==0) { sz++; Father[sz]=Fa; Son[Fa][x>Key[Fa]]=sz; Key[sz]=x; Size[sz]=Cnt[sz]=1; Update(Fa); Splay(sz); break; } } } int Find(int x) { int Ans=0,now=Root; while (1) { if (x<Key[now]) now=Son[now][0]; else { Ans+=Size[Son[now][0]]; if (x==Key[now]) { Splay(now); return Ans+1; } Ans+=Cnt[now]; now=Son[now][1]; } } } int Pre() { int now=Son[Root][0]; while (Son[now][1]) now=Son[now][1]; return now; } int Next() { int now=Son[Root][1]; while (Son[now][0]) now=Son[now][0]; return now; } void Del(int x) { int wh=Find(x);//把這個數字轉到根節點 if (Cnt[Root]>1)//如果這個數值不只一個就-1就好了 { --Cnt[Root]; Update(Root); return; } if (!Son[Root][0]&&!Son[Root][1])//如果根沒兒子就說明樹上只有一個點 { Clear(Root); Root=0; return; } if (!Son[Root][0])//如果根只有一個右兒子就把右兒子當根 { int Oldroot=Root; Root=Son[Oldroot][1]; Father[Root]=0; Clear(Oldroot); return; } if (!Son[Root][1])//如果根只有一個左兒子就把左兒子當根 { int Oldroot=Root; Root=Son[Oldroot][0]; Father[Root]=0; Clear(Oldroot); return; } int Oldroot=Root;//如果有兩個兒子就把前驅作為新根,把右子樹接到新根上 int Leftbig=Pre(); Splay(Leftbig); Son[Root][1]=Son[Oldroot][1]; Father[Son[Root][1]]=Root; Clear(Oldroot); Update(Root); return; } int main() { int n,x,y,Ans=0,p,Now; memset(&INF,0x7f,sizeof(INF)); scanf("%d",&n); for (int i=1;i<=n;++i) { scanf("%d%d",&x,&y); if (Root==0) Now=x; if (x==Now) Insert(y); else { if (Root==0) continue; Insert(y); int pre=Key[Pre()],nxt=Key[Next()]; if (!pre) pre=INF; if (!nxt) nxt=INF; Ans=(Ans+min(abs(y-pre),abs(y-nxt)))%MOD; if (abs(y-pre)<=abs(y-nxt)) Del(pre); else Del(nxt); Del(y); } } printf("%d",Ans); }
1208. [HNOI2004]寵物收養場【平衡樹-splay】