【BZOJ1208】【HNOI2004】宠物收养所(splay)
【问题描述】
最近,阿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)。 【任务描述】 你得到了一年当中,领养者和被收养宠物到来收养所的情况,希望你计算所有收养了宠物的领养者的不满意程度的总和。这一年初始时,收养所里面既没有宠物,也没有领养者。
n<=80000。同一时间呆在收养所中的,要么全是宠物,要么全是领养者,这些宠物和领养者的个数不会超过10000个
【分析】
平衡树模板,都是基础操作,直接写splay了。
由于同一时间要么全是宠物要么全是领养者,所以只要一颗树就好了。
找前驱和后继,我不是把点旋到根。例如x的前驱,从根往下走寻找x。每次可能往左儿子走也可能往右儿子走,x的前驱即从根到x的路径中最后一次往右走的那个点。画一下图就知道了。不过这样子不见得比旋到根好就是了。- -
【代码】
代码一向略龊。。。
/************************************************************** Problem: 1208 User: N_C_Derek Language: C++ Result: Accepted Time:184 ms Memory:2836 kb ****************************************************************/ #include <iostream> #include <cstring> #include <algorithm> #include <cstdio> #include <cmath> using namespace std; int root,ifman,ans,t,n; struct node { int l,r,data,father; }tree[100000]; void zig(int x) { int p = tree[x].father; if (tree[p].father) { if (tree[tree[p].father].l == p) tree[tree[p].father].l = x; else tree[tree[p].father].r = x; } tree[x].father = tree[p].father; tree[p].father = x; tree[p].l = tree[x].r; if (tree[x].r) tree[tree[x].r].father = p; tree[x].r = p; } void zag(int x) { int p = tree[x].father; if (tree[p].father) { if (tree[tree[p].father].l == p) tree[tree[p].father].l = x; else tree[tree[p].father].r = x; } tree[x].father = tree[p].father; tree[p].father = x; tree[p].r = tree[x].l; if (tree[x].l) tree[tree[x].l].father = p; tree[x].l = p; } void splay(int x) { while (tree[x].father) { int j = tree[x].father; if (tree[j].father == 0) { if (tree[j].l == x) zig(x); else zag(x); break; } if (tree[tree[j].father].l == j) { if (tree[j].l == x) {zig(j);zig(x);} else {zag(x);zig(x);} }else { if (tree[j].r == x) {zag(j);zag(x);} else {zig(x);zag(x);} } } root = x; } void Insert(int x) { if (!root) { root = ++t; tree[root].father = 0; tree[root].data = x; return; } int i = root; while (tree[i].l || tree[i].r) { if (tree[i].data > x) {if (!tree[i].l) break;i = tree[i].l;} else {if (!tree[i].r) break;i = tree[i].r;} } if (tree[i].data > x) tree[i].l = ++t; else tree[i].r = ++t; tree[t].father = i; tree[t].data = x; splay(t); } void get_max() { int i = root; while (tree[i].r) i = tree[i].r; splay(i); } void Delete(int x) { splay(x); if (!tree[x].l) { root = tree[x].r; tree[root].father = 0; return; } int tmp = tree[root].r; root = tree[root].l; get_max(); tree[root].r = tmp; tree[tmp].father = root; } int find(int x) { int i = root,ii = 0; while (i) { if (tree[i].data > x) { ii = i;i = tree[i].l; }else i = tree[i].r; } int j = root,jj = 0; while (j) { if (tree[j].data < x) { jj = j;j = tree[j].r; }else j = tree[j].l; } if (jj && (!ii || abs(tree[jj].data - x) <= abs(tree[ii].data - x))) return jj; else return ii; } int main() { scanf("%d",&n); for (int i = 1;i <= n;i ++) { int k,x; scanf("%d %d",&k,&x); if (k == 1) { if (ifman || !root) { Insert(x); ifman = 1; } else { int j = find(x); ans = (ans + abs(tree[j].data - x)) % 1000000; Delete(j); } }else { if (!ifman || !root) { Insert(x); ifman = 0; } else { int j = find(x); ans = (ans + abs(tree[j].data - x)) % 1000000; Delete(j); } } } cout << ans; }