洛谷P2286 [HNOI2004]宠物收养场(BZOJ1208)
平衡树
本来1A的。。。没有膜WA了3发,数组开小了又RE了一发。。。
加一个标记表示当前是宠物树还是领养者树。如果当前读入的和标记一样就直接加进去,否则找前驱和后继比较后删去一个点。当树为空时改变标记。
没有用内存池数组要开80000啊。。。别被10000坑到了。。。血一样的教训。。。
代码(这里用Treap):
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define N 80000
using namespace std;
const int MOD=1e6;
struct node{
int rnd,to[2],p,size;
int w;
}t[N+5];
int m,nd,rt,fl;
long long ans;
void rtt(int &x,int fl){
int s=t[x].to[fl];
t[x].to[fl]=t[s].to[fl^1];
t[s].to[fl^1]=x; t[s].size=t[x].size;
t[x].size=t[t[x].to[0]].size+t[t[x].to[1]].size+t[x].p;
x=s;
}
void nsrt(int &x,int w){
if (!x){
t[x=++nd].w=w; t[x].p=t[x].size=1;
t[x].rnd=rand(); return;
}
t[x].size++;
if (t[x].w==w) t[x].p++;
else{
int flag=t[x].w<w;
nsrt(t[x].to[flag],w);
if (t[t[x].to[flag]].rnd<t[x].rnd) rtt(x,flag);
}
}
void dlt(int &x,int w){
if (!x) return;
if (t[x].w==w){
if (t[x].p>1) { t[x].size--; t[x].p--; return; }
if (!t[x].to[0]) { x=t[x].to[1]; return; }
if (!t[x].to[1]) { x=t[x].to[0]; return; }
int flag=t[t[x].to[0]].rnd<t[t[x].to[1]].rnd;
rtt(x,flag^1),dlt(x,w);
}
else{
t[x].size--;
int flag=t[x].w<w;
dlt(t[x].to[flag],w);
}
}
int srch_frnt(int x,int w){
if (!x) return -1;
if (t[x].w==w) return w;
if (t[x].w<w){
int p=srch_frnt(t[x].to[1],w);
if (~p) return p;
return t[x].w;
}
else return srch_frnt(t[x].to[0],w);
}
int srch_bck(int x,int w){
if (!x) return -1;
if (t[x].w==w) return w;
if (t[x].w>w){
int p=srch_bck(t[x].to[0],w);
if (~p) return p;
return t[x].w;
}
else return srch_bck(t[x].to[1],w);
}
int main(){
scanf("%d",&m);
while (m--){
int flag;int x;
scanf("%d%d",&flag,&x);
if (!t[rt].size) fl=flag;//如果当前树空改变标记
if (flag^fl){//如果不相同则匹配后删除
int p=srch_frnt(rt,x),q=srch_bck(rt,x);
if ((p!=-1&&x-p<=q-x)||q==-1) (ans+=x-p)%=MOD,dlt(rt,p);
if ((q!=-1&&q-x<x-p)||p==-1) (ans+=q-x)%=MOD,dlt(rt,q);
}
else nsrt(rt,x);//否则直接加进去
}
return printf("%lld\n",ans),0;
}