1005 Welcome Party
解题过程:先是考虑到一个n2的做法,是可以的。首先下面会把去唱歌的人里面唱歌值最高的称作“主角”。首先枚举每个人作为主角,然后唱歌值比主角高的必须要丢到相声里面。然后相声里面就已经有一个最大值了,假如相声值最大值已经超过了主角的唱歌值,那么这个差就更新答案。否则从唱歌值小于等于主角的里面选一个和主角的唱歌值最接近的人去相声,并且这个人的相声值可以刷新相声最大值,得到的结果更新答案。这个算法是O(n2)的。
显然可以考虑一个过程,从唱歌值最高的人一个一个选作主角,那么必须去相声的人(也就是曾经的主角)是逐个递增的,把这些人从平衡树里面删除就可以了。然后在平衡树里面询问主角唱歌值的最接近的值,很明显就是先问准确值,再问前驱和后继。注意只有查询结果超过相声最大值才更新。这个做法有点问题,就是有可能会把唱歌主角整去搞相声,这样就错了,所以要先把主角删除,查完之后再把主角插回去。
最后Remove没改ll,演了队友一发,太惨了。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 500005;
const ll INF = 2e18 + 5e17;
int ch[MAXN][2];
ll val[MAXN];
int dat[MAXN];
int cnt[MAXN];
int tot, root;
inline void Init() {
tot = 0;
root = 0;
}
inline int NewNode(ll v) {
val[++tot] = v;
dat[tot] = rand();
ch[tot][0] = ch[tot][1] = 0;
cnt[tot] = 1;
return tot;
}
inline void Rotate(int &id, int d) {
int tmp = ch[id][d ^ 1];
ch[id][d ^ 1] = ch[tmp][d];
ch[tmp][d] = id;
id = tmp;
}
inline void Insert(int &id, ll v) {
if(!id)
id = NewNode(v);
else {
if(v == val[id])
++cnt[id];
else {
int d = v < val[id] ? 0 : 1;
Insert(ch[id][d], v);
if(dat[id] < dat[ch[id][d]])
Rotate(id, d ^ 1);
}
}
}
void Remove(int &id, ll v) {
if(!id)
return;
else {
if(v == val[id]) {
if(cnt[id] > 1) {
cnt[id]--;
} else if(ch[id][0] || ch[id][1]) {
if(!ch[id][1] || dat[ch[id][0]] > dat[ch[id][1]])
Rotate(id, 1), Remove(ch[id][1], v);
else
Rotate(id, 0), Remove(ch[id][0], v);
} else
id = 0;
} else {
v < val[id] ? Remove(ch[id][0], v) : Remove(ch[id][1], v);
}
}
}
bool GetEx(ll v) {
int id = root;
while(id) {
if(val[id] == v)
return true;
else if(val[id] < v)
id = ch[id][1];
else
id = ch[id][0];
}
return false;
}
ll GetPrev(ll v) {
int id = root;
ll prev = -INF;
while(id) {
if(val[id] < v)
prev = val[id], id = ch[id][1];
else
id = ch[id][0];
}
return prev;
}
ll GetNext(ll v) {
int id = root;
ll next = INF;
while(id) {
if(val[id] > v)
next = val[id], id = ch[id][0];
else
id = ch[id][1];
}
return next;
}
struct Hito {
ll x, y;
bool operator<(const Hito &h)const {
return x < h.x;
}
} hito[MAXN];
void solve() {
int n;
scanf("%d", &n);
for(int i = 1; i <= n; ++i)
scanf("%lld%lld", &hito[i].x, &hito[i].y);
sort(hito + 1, hito + 1 + n);
Init();
for(int i = 1; i <= n; ++i)
Insert(root, hito[i].y);
ll ans = INF, maxy = -INF;
for(int i = n, nxt; i >= 1; i = nxt) {
for(nxt = i - 1; nxt >= 1 && hito[nxt].x == hito[i].x; --nxt);
for(int j = i; j > nxt; --j) {
//把主角去掉,主角不能去相声
Remove(root, hito[j].y);
if(hito[i].x >= maxy && GetEx(hito[i].x)) {
//唱歌值比相声最大值大,才有意义
printf("0
");
return;
}
ans = min(ans, abs(hito[i].x - maxy));
//相等的话很麻烦,小心
ll prev = (hito[i].x >= maxy) ? GetPrev(hito[i].x) : -INF, next = GetNext(hito[i].x);
ll d1 = abs(hito[i].x - prev);
ll d2 = abs(next - hito[i].x );
if(prev <= maxy) {
//前驱不能刷新最值,不要了
d1 = INF;
}
if(next <= maxy) {
//后继不能刷新最值,也不要了
d2 = INF;
}
ans = min(ans, min(d1, d2));
//主角回来了,另一个相等的人去做主角
Insert(root, hito[j].y);
}
for(int j = i; j > nxt; --j) {
//大家都去讲相声了
Remove(root, hito[j].y);
maxy = max(maxy, hito[j].y);
}
}
printf("%lld
", ans);
}
int main() {
#ifdef Yinku
freopen("Yinku.in", "r", stdin);
#endif // Yinku
int T;
//cout<<INF+INF<<endl;
while(~scanf("%d", &T)) {
while(T--)
solve();
}
}