Codeforces-Contest19-D.Points-线段树+set

题目描述:

Pete and Bob invented a new interesting game. Bob takes a sheet of paper and locates a Cartesian coordinate system on it as follows: point (0, 0) is located in the bottom-left corner, Ox axis is directed right, Oy axis is directed up. Pete gives Bob requests of three types:

  • add x y — on the sheet of paper Bob marks a point with coordinates (x, y). For each request of this type it’s guaranteed that point (x, y) is not yet marked on Bob’s sheet at the time of the request.
  • remove x y — on the sheet of paper Bob erases the previously marked point with coordinates (x, y). For each request of this type it’s guaranteed that point (x, y) is already marked on Bob’s sheet at the time of the request.
  • find x y — on the sheet of paper Bob finds all the marked points, lying strictly above and strictly to the right of point (x, y). Among these points Bob chooses the leftmost one, if it is not unique, he chooses the bottommost one, and gives its coordinates to Pete.

Bob managed to answer the requests, when they were 10, 100 or 1000, but when their amount grew up to 2·105, Bob failed to cope. Now he needs a program that will answer all Pete’s requests. Help Bob, please!

输入描述:

The first input line contains number n (1 ≤ n ≤ 2·105) — amount of requests. Then there follow n lines — descriptions of the requests. add x y describes the request to add a point, remove x y — the request to erase a point, find x y — the request to find the bottom-left point. All the coordinates in the input file are non-negative and don’t exceed 109.

输出描述:

For each request of type find x y output in a separate line the answer to it — coordinates of the bottommost among the leftmost marked points, lying strictly above and to the right of point (x, y). If there are no points strictly above and to the right of point (x, y), output -1.

输入样例:

7
add 1 1
add 3 4
find 0 0
remove 1 1
find 0 0
add 1 1
find 0 0

输出样例:

1 1
3 4
1 1

大概思路:

题目给定的是二维坐标,解决过程按维拆分成两步。
用set[i]存储横坐标为i的点的纵坐标。
线段树结点区间[l,r]内的y值,维护的是横坐标区间[l,r]内的纵坐标最大值y。这样维护的原因是:当set[i]的最大值大于findy时,set[i]才有可能存有最优解。
第一步,用线段树logn*logn复杂度来更新或查找最优解所对应的横坐标ansx(此时查不到最优解的纵坐标,因为线段树里维护的是最大值而不是最小值);
第二步,在set[ansx]中logn复杂度查找最优解的纵坐标。

总复杂度:n*logn*logn
本题时间卡的紧,细节见代码注释。

代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;
typedef long long ll;
const int N=4e5+50;
int b[N<<1],cnt;//用于离散化 
set<int>se[N];
set<int>::iterator it;
char s[30];
struct node{//线段树结点 
	int l,r,y;
	node(){}
	node(int a,int b,int c){
		l=a,r=b,y=c;
	}
}tr[N<<3]; 
struct Node{//存下来,离散化后重新模拟 
	int k,x,y;
}w[N];
void pushup(int m)
{
	int y1=tr[m<<1].y,y2=tr[m<<1|1].y;
	if(y2==-1||y1!=-1&&y2!=-1&&y1>=y2)tr[m].y=tr[m<<1].y;
	else tr[m].y=tr[m<<1|1].y;
	return ;
}
void build(int m,int l,int r)
{
	tr[m]=node(l,r,-1);
	if(l==r)return;
	int mid=l+r>>1;
	build(m<<1,l,mid);
	build(m<<1|1,mid+1,r);
	return;
}
void add(int m,int p,int y)
{
	if(tr[m].l==p&&tr[m].r==p)
	{
		se[p].insert(y);
		tr[m].y=*(se[p].rbegin());//每一次插入都要更新叶节点的y值并pushup 
		return;
	}
	int mid=tr[m].l+tr[m].r>>1;
	if(p<=mid) add(m<<1,p,y);
	else add(m<<1|1,p,y);
	pushup(m);
	return;
}
void remove(int m,int p,int y)
{
	if(tr[m].l==p&&tr[m].r==p)
	{
		se[p].erase(y);
		if(se[p].empty()) tr[m].y=-1;
		else tr[m].y=*se[p].rbegin();//每一次删除都要判空,更新叶节点的y值并pushup
		return;
	}
	int mid=tr[m].l+tr[m].r>>1;
	if(p<=mid) remove(m<<1,p,y);
	else remove(m<<1|1,p,y);
	pushup(m);
	return;
}
int query(int m,int l,int r,int y)//先找左子树,没有再找右子树 
{
	if(tr[m].y==-1||tr[m].y<=y)return -1;//及时返回,防止超时 
	if(tr[m].l==tr[m].r) return tr[m].l;//每次区间查询到且只到一次叶结点
	int mid=tr[m].l+tr[m].r>>1;
	if(l<=mid)
	{
		int re=query(m<<1,l,mid,y);
		if(re!=-1)return re;//左子树找到 
	}
	return query(m<<1|1,max(l,mid+1),r,y);//找右子树 
}
int f(int x){return lower_bound(b,b+cnt,x)-b+1;}//离散化 
int main()
{
	int n;
	cin>>n;
	for(int i=0;i<n;i++)//全部输入并准备离散化 
	{
		scanf("%s%d%d",s,&w[i].x,&w[i].y);
		b[cnt++]=w[i].x;
		b[cnt++]=w[i].y;
		if(s[0]=='a')w[i].k=1;
		else if(s[0]=='r')w[i].k=2;
		else w[i].k=3;
	}
	sort(b,b+cnt);
	cnt=unique(b,b+cnt)-b;//去重 
	build(1,1,cnt);//建树 
	for(int i=0;i<n;i++)//三种操作 
	{
		int k=w[i].k,x=f(w[i].x),y=f(w[i].y);
		if(k==1)add(1,x,y);
		else if(k==2)remove(1,x,y);
		else
		{
			int re=x+1<=cnt?query(1,x+1,cnt,y):-1;
			if(re==-1) printf("-1\n");
			else
			{
				it=se[re].upper_bound(y);
				printf("%d %d\n",b[re-1],b[*it-1]);
			}
		}
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值