P2286 [HNOI2004] 宠物收养场
题目翻译:
给定 \(n\) 次操作,每次操作会添加一个元素。并且维护一个集合,若当前添加的元素与集合中元素种类相同,则直接添加,若不相同这要将最终答案加上给元素与集合中元素的差的绝对值的最小值,并删除对应的集合中的元素。(特别注意:若有两个元素与添加元素的差相同,则删除较小的那个)
思路:
若要找到一个与添加元素最接近的元素,且可以随意删除添加,很容易想到平衡树。我们维护一个平衡树,在查询的时候直接找前驱和后继,求最小值然后,删除即可。对于两种不同种类的元素,我们只需要用一个变量 \(op\) 维护当前集合中的元素类型,并用 \(cnt\) 变量维护当前集合中元素个数,这样每次添加元素时,若 \(cnt=0\) 就给 \(op\) 赋值为当前元素类型,即可。
完整代码:
#include<bits/stdc++.h>
#define fa(x) tr[(x)].fa
#define lc(x) tr[(x)].s[0]
#define rc(x) tr[(x)].s[1]
using namespace std;
const int N=1e5+10;
const int P=1000000;
struct tree{int s[2],fa,siz,key;tree(){s[0]=s[1]=fa=siz=key=0;}
}tr[N];
int rt,idx;
void pushup(int x){tr[x].siz=tr[lc(x)].siz+tr[rc(x)].siz+1;
}
int newnode(int key){tr[++idx].key=key;tr[idx].siz=1;return idx;
}
int get(int x){return x==rc(fa(x));
}
void clear(int x){lc(x)=rc(x)=fa(x)=tr[x].siz=tr[x].key=0;
}
void rotate(int x){int y=fa(x),z=fa(y),c=get(x);if(tr[x].s[c^1])tr[tr[x].s[c^1]].fa=y;tr[y].s[c]=tr[x].s[c^1];tr[x].s[c^1]=y;fa(y)=x;fa(x)=z;if(z)tr[z].s[y==rc(z)]=x;pushup(y);pushup(x);
}
void splay(int x){for(int f=fa(x);f=fa(x),f;rotate(x)){if(fa(f))rotate(get(x)==get(x)?f:x);}rt=x;
}
void ins(int key){int now=rt,f=0;while(now){f=now;now=tr[now].s[key>tr[now].key];}now=newnode(key);tr[f].s[key>tr[f].key]=now;fa(now)=f;splay(now);
}
int pre(int key){int now=rt,f=0,ans=0;while(now){f=now;if(tr[now].key>key){now=lc(now);}else{ans=tr[now].key;now=rc(now);}}splay(f);return ans;
}
int nxt(int key){int now=rt,f=0,ans=0;while(now){f=now;if(tr[now].key<key){now=rc(now);}else{ans=tr[now].key;now=lc(now);}}splay(f);return ans;
}
void del(int key){int now=rt,f=0;while(now && key!=tr[now].key){f=now;now=tr[now].s[key>tr[now].key];}if(!now){splay(f);return;}splay(now);int cur=lc(now);if(!cur){rt=rc(now);fa(rt)=0;clear(now);return;}while(rc(cur)){cur=rc(cur);}fa(rc(now))=cur;rc(cur)=rc(now);fa(lc(now))=0;clear(now);pushup(cur);splay(cur);
}
int main(){ins(+214748364);ins(-214748364);int n;scanf("%d",&n);int op,w,now,ans=0,cnt=0;for(int i=1;i<=n;i++){scanf("%d%d",&op,&w);if(cnt==0){now=op;}if(op==now){ins(w);cnt++;}else{int w1=pre(w),w2=nxt(w);//cout<<w1<<" "<<w2<<endl;if(abs(w-w1)<=abs(w-w2)){ans+=abs(w-w1);ans%=P;cnt--;del(w1);}else{ans+=abs(w-w2);ans%=P;cnt--;del(w2);}}}printf("%d",ans);
}