题目链接:UVA11020
【题意】有n个人,每个人有两个属性x,y。如果对于一个人p(x,y),不存在另外一个人(x1,y1)使得x1<x,y1<=y,或者x1<=x,y1<y,我们说p是有优势的,每次给出一个人信息。要求输出只考虑当前自己已获得的信息前提下,多少人是有优势的。
【分析】白书上已经说的很详细了,我这里自己用那个treap实现了一个multiset,主要是加了next和pre,具体看代码注释;Debug了一天呀。
【AC CODE】82ms
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <queue>
#include <stack>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
typedef long long LL;
#define rep(i,a,n) for(int i = a; i < n; i++)
#define repe(i,a,n) for(int i = a; i <= n; i++)
#define per(i,n,a) for(int i = n; i >= a; i--)
#define clc(a,b) memset(a,b,sizeof(a))
const int INF = 0x3f3f3f3f;
typedef pair<int,int> P;
struct Treap{
/*
Treap同时有二叉搜索树(BST,键值:左儿子<根<右儿子)和堆的性质(根节点的优先级大于儿子节点)
这样能保证在优先级唯一且不相等的情况下树是唯一的
这里的r(优先级)是用rand()生成的,能证明查找,插入,删除的时间复杂度都是log(n)
注意:rand()最大值是RAND_MAX(系统不同值不同,可以输出看看),所以不够的话不能直接用
rand()是在0~RAND_MAX中的书按照某个顺序输出的,在输出前RAND_MAX+1中是不会重复的
*/
const int MAX_RAND = 100000;//设置最大的rand()
int size;
struct NODE{
NODE *ch[2];//0左1右儿子
NODE *next, *pre;//横向链接,根据v从小到大排序好之后
int r,cnt;//优先级,值越大越高
P v;//键值
int cmp(P x){
if(x == v) return -1;
return x<v?0:1;
}
}*rt, *begin, *end;
void newnode(NODE* &u, P v){
u = new NODE();
u->ch[0] = u->ch[1] = u->next = u->pre = NULL, u->v = v, u->r = rand();
u->cnt = 1;
size++;
}
void clear(){
P buf=make_pair(-1,-1);
newnode(end,buf);
buf = make_pair(-2,-2);
newnode(begin,buf);
begin->next = end,end->pre = begin;
rt = NULL;
size = 0;
}
void rotate(NODE* &u, int d)//旋转,d = 0左旋,d = 1右旋
{
NODE *k = u->ch[d^1];
u->ch[d^1] = k->ch[d], k->ch[d] = u, u = k;
}
bool find(NODE *u, P v, NODE* &ans)//查找键值v,查找到的话就是ans,否则ans是v应该插入的位置
{
if(!u)
{
ans = begin;
return false;
}
int d;
while(u)
{
ans = u;
d = u->cmp(v);
if(-1 == d)
{
//if(ans) ans = ans->next;
return true;
}
else u = u->ch[d];
}
if(d && ans) ans = ans->next;
return false;
}
NODE* add(NODE* &u, P v)//插入键值v,修改u(需要保证v不存在),返回新增的位置
{
if(!u)//新建节点
{
newnode(u,v);
return u;
}
int d = u->cmp(v);
NODE *ans = add(u->ch[d], v);
if(u->ch[d]->r > u->r)//如果儿子比根优先级大了就需要旋转
rotate(u,d^1);
return ans;
}
void insert(P v)//插入键值p
{
NODE *k;
if(find(rt,v,k)) k->cnt++,size++;
else
{
NODE *ans = add(rt,v);
if(-2 == k->v.first)
{
ans->next = end;
end->pre = ans;
begin = ans;
return;
}
ans->next = k;
if(k->pre) k->pre->next = ans, ans->pre = k->pre;
k->pre = ans;
if(end->pre->v < v)
end->pre = ans;//插入值为最大需要更新end->pre
if(v < begin->v)//插入值为最小时需要更新begin
{
//if(v.first == begin->v.first) size -= begin->cnt, del(rt,begin->v);//如果插入值覆盖了begin则需要删除原来的begin
begin = ans;
ans->pre = NULL;
}
}
}
void del(NODE* &u, P v)//删除键值v(需要保证v存在)
{
int d = u->cmp(v);//寻找是左还是右儿子
if(~d)//没找到v,继续往下找
{
del(u->ch[d],v);
return;
}
//找到了节点v
if(!u->ch[0]) //没有左儿子直接把右儿子付到根节点吧
{
if(u->pre) u->pre->next = u->next;
if(u->next) u->next->pre = u->pre;
u = u->ch[1];
}
else if(!u->ch[1])
{
if(u->pre) u->pre->next = u->next;
if(u->next) u->next->pre = u->pre;
u = u->ch[0];
}
else
{
int d2 = u->ch[0]->r > u->ch[1]->r?1:0;
rotate(u,d2);
del(u->ch[d2],v);
}
}
NODE* erase(P v)//删除键值v,返回v下一个位置
{
NODE *ans = end, *k;//k为要删除的值
if(find(rt,v,k))//找到键值直接删除,相同值有多个就全部删除
{
size -= k->cnt;
ans = k->next;
if(begin == k)
begin = k->next;
del(rt,v);
}
return ans;
}
NODE* lower_bound(P v)
{
NODE *k;
find(rt,v,k);
return k;
}
NODE* upper_bound(P v)
{
NODE *k;
if(find(rt,v,k) && k)
k = k->next;
return k;
}
}s;
int main()
{
#ifdef SHY
freopen("e:\\1.txt", "r", stdin);
#endif
int t, count = 0;
scanf("%d%*c", &t);
while(t--)
{
int n;
scanf("%d%*c", &n);
P a;
s.clear();
printf("Case #%d:\n", ++count);
rep(i,0,n)
{
scanf("%d %d%*c", &a.first, &a.second);
auto p = s.lower_bound(a);
if(p == s.begin || p->pre->v.second > a.second)
{
s.insert(a);
p = s.upper_bound(a);
while(p != s.end && p->v.second >= a.second)
p = s.erase(p->v);
}
printf("%d\n", s.size);
}
if(t) putchar('\n');
}
return 0;
}