CF19D Points

\(Firstly\),离散

坐标范围太大

考虑离散化

\(Secondly\),线段树

在一个笛卡尔坐标系中,定义三种操作:

由题意的这句话非常容易想到这是一道数据结构题

\(1<=n<=2⋅10^5\)\(\Rightarrow\)最多有\(2⋅10^5\)个横坐标

针对每一个\(x_i\)对应的\({y_i}_{max}\)我们用线段树来维护

3.find x y :找到所有已标记并在(x,y)右上方的点中,最左边的点,若点不唯一,选择最下面的一个点; 如果没有符合要求的点,给出"-1",否则给出x y.

酱紫,我们可以再二分线段树\(\Rightarrow\)\(logn\)的时间复杂度内完成操作\(3\)

操作\(1\),\(2\)就只是更改某个\(x\)\({y_i}_{max}\)\(\Rightarrow\)单点修改

\(Additionally\),\(set\)

也许你认为到这就可以了

但是你忽略了一点 : 操作\(1\),\(2\)是否该更改

一些数据结构大神跳出来话说 针对每一个\(x\)上的\(y\)我们构建一个平衡树

支持插入,删除,维护最大值

其实 \(set\)就可以完全实现这些操作

\(Finally\)

这道题有卡常倾向

线段树\(+\)平衡树貌似是过不了的

线段树\(+\) \(set\) \(+\)直接二分只能\(A51\)个点

线段树\(+\) \(set\) \(+\)线段树二分才能\(AC\)

\(Code\)

\(AC Code\)

#include <map>
#include <set>
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#define reg register
const int MAXN = 2e5 + 10;
struct node {
	int sit,x,y;
	void assignedment(int SIT) {
		sit = SIT;
	}
};
int T,Right,seg[MAXN];
set<int> st[MAXN];
node option[MAXN];
map<string,int> mp;
map<int,int> past_x,past_y;
//记录以前的值  
namespace pre {
	pair<int,int> a[MAXN],b[MAXN];
	inline void init() {
		mp["add"] = 1,mp["find"] = 2,mp["remove"] = 3;
		scanf("%d",&T);
		for(reg int i = 1; i <= T; i++) {
			string sit;
			cin >> sit;
			int x,y;
			scanf("%d%d",&x,&y);
			a[i] = make_pair(x,i);
			b[i] = make_pair(y,i);
			option[i].assignedment(mp[sit]);
		}
	}
	inline void hash() {
		sort(a + 1,a + 1 + T);
		sort(b + 1,b + 1 + T);
		int va,vb;
		va = vb = 0;
		for(reg int i = 1; i <= T; i++) {
			if(i == 1||a[i].first > a[i - 1].first) va++;
			if(i == 1||b[i].first > b[i - 1].first) vb++;
			option[a[i].second].x = va;
			past_x[va] = a[i].first;
			past_y[vb] = b[i].first;
			option[b[i].second].y = vb;
		}
		Right = va;
	}
	//输入及离散化 
}
namespace segment {
	int tree[MAXN << 2];
	inline void change(int l,int r,int k,int pos,int x) {
		if(l == r&&l == pos) {
			tree[k] = x;
			return;
		}
		int mid = l + r >> 1;
		if(pos <= mid) change(l,mid,k << 1,pos,x);
		else change(mid + 1,r,k << 1 | 1,pos,x);
		tree[k] = max(tree[k << 1],tree[k << 1 | 1]);
	}
	inline int query(int l,int r,int k,int ql,int qr) {
		if(ql <= l&&r <= qr)
			return tree[k];
		int mid = l + r >> 1,k1,k2;
		k1 = k2 = -1;
		if(ql <= mid) k1 = query(l,mid,k << 1,ql,qr);
		if(qr > mid) k2 = query(mid + 1,r,k << 1 | 1,ql,qr);
		return max(k1,k2);
	}
	//线段树 
	inline int findans(int l,int r,int k,int pos,int x)
	{
		if(l == r) return l;
		int mid = l + r >> 1,k1,k2;
		k1 = k2 = Right + 1;
		if(pos < mid&&tree[k << 1] > x) k1 = findans(l,mid,k << 1,pos,x);
		if(k1 < Right + 1) return k1;
		//这一句必须加 不然T(左边已经找到了 没必要找右边的) 
		if(tree[k << 1 | 1] > x) k2 = findans(mid + 1,r,k << 1 | 1,pos,x);
		return min(k1,k2);
	}
	//线段树上的二分 
}
inline void solve() {
	for(reg int i = 1; i <= T; i++) {
		switch(option[i].sit) {
			case 1: {
				if(st[option[i].x].size() == 0)
					segment::change(1,Right,1,option[i].x,option[i].y);
				else {
					auto it = st[option[i].x].end();
					if(option[i].y > *(--it)) segment::change(1,Right,1,option[i].x,option[i].y);
				}
				st[option[i].x].insert(option[i].y);
				//加点 用set 
				break;
			}
			case 2: {
				int l = option[i].x + 1,r = Right;
				int res = segment::query(1,Right,1,l,r);
				if(res <= option[i].y) printf("-1\n");
				else {
					int j = segment::findans(1,Right,1,option[i].x,option[i].y);
					printf("%d %d\n",past_x[j],past_y[*(st[j].upper_bound(option[i].y))]);
				}
				//求答案 
				break;
			}
			case 3: {
				auto it = st[option[i].x].upper_bound(option[i].y);
				bool f = 0;
				if(it == st[option[i].x].end()) f = 1;
				st[option[i].x].erase((--it));
				if(f) {
					int pas;
					if(st[option[i].x].size() == 0)
						pas = 0;
					else {
						auto it = st[option[i].x].end();
						pas = *--it;
					}
					segment::change(1,Right,1,option[i].x,pas);
				}
				//删点 用set 
				break;
			}
		}
	}
}
int main() {
	pre::init();
	pre::hash();
	solve();
	return 0;
}

\(Tle code\)

#include <map>
#include <set>
#include <cstdio>
#include <string>
#include <iostream>
#include <algorithm>
using namespace std;
#define reg register
const int MAXN = 2e5 + 10;
struct node {
	int sit,x,y;
	void assignedment(int SIT) {
		sit = SIT;
	}
};
int T,Right,seg[MAXN];
set<int> st[MAXN];
node option[MAXN];
map<string,int> mp;
map<int,int> past_x,past_y;
namespace pre {
	pair<int,int> a[MAXN],b[MAXN];
	inline void init() {
		mp["add"] = 1,mp["find"] = 2,mp["remove"] = 3;
		scanf("%d",&T);
		for(reg int i = 1; i <= T; i++) {
			string sit;
			cin >> sit;
			int x,y;
			scanf("%d%d",&x,&y);
			a[i] = make_pair(x,i);
			b[i] = make_pair(y,i);
			option[i].assignedment(mp[sit]);
		}
	}
	inline void hash() {
		sort(a + 1,a + 1 + T);
		sort(b + 1,b + 1 + T);
		int va,vb;
		va = vb = 0;
		for(reg int i = 1; i <= T; i++) {
			if(i == 1||a[i].first > a[i - 1].first) va++;
			if(i == 1||b[i].first > b[i - 1].first) vb++;
			option[a[i].second].x = va;
			past_x[va] = a[i].first;
			past_y[vb] = b[i].first;
			option[b[i].second].y = vb;
		}
		Right = va;
	}
}
namespace segment {
	int tree[MAXN << 2];
	inline void change(int l,int r,int k,int pos,int x) {
		if(l == r&&l == pos) {
			tree[k] = x;
			return;
		}
		int mid = l + r >> 1;
		if(pos <= mid) change(l,mid,k << 1,pos,x);
		else change(mid + 1,r,k << 1 | 1,pos,x);
		tree[k] = max(tree[k << 1],tree[k << 1 | 1]);
	}
	inline int query(int l,int r,int k,int ql,int qr) {
		if(ql <= l&&r <= qr)
			return tree[k];
		int mid = l + r >> 1,k1,k2;
		k1 = k2 = -1;
		if(ql <= mid) k1 = query(l,mid,k << 1,ql,qr);
		if(qr > mid) k2 = query(mid + 1,r,k << 1 | 1,ql,qr);
		return max(k1,k2);
	}
}
inline void solve() {
	for(reg int i = 1; i <= T; i++) {
		switch(option[i].sit) {
			case 1: {
				if(st[option[i].x].size() == 0)
					segment::change(1,Right,1,option[i].x,option[i].y);
				else {
					auto it = st[option[i].x].end();
					if(option[i].y > *(--it)) segment::change(1,Right,1,option[i].x,option[i].y);
				}
				st[option[i].x].insert(option[i].y);
				break;
			}
			case 2: {
				int l = option[i].x + 1,r = Right;
				int res = segment::query(1,Right,1,l,r);
				if(res <= option[i].y) printf("-1\n");
				else {
					while(l < r) {
						int mid = l + r >> 1;
						int res = segment::query(1,Right,1,l,mid);
						if(res > option[i].y) r = mid;
						else l = mid + 1;
					}
					int j = l;
					printf("%d %d\n",past_x[j],past_y[*(st[j].upper_bound(option[i].y))]);
				}
                
                //与上面AC的代码不一样的只有这里 上面是在树上二分的 时间复杂度O(logn) 而这里直接二分+线段树求区间最大值O(log^2n)
				break;
			}
			case 3: {
				auto it = st[option[i].x].upper_bound(option[i].y);
				bool f = 0;
				if(it == st[option[i].x].end()) f = 1;
				st[option[i].x].erase((--it));
				if(f) {
					int pas;
					if(st[option[i].x].size() == 0)
						pas = 0;
					else {
						auto it = st[option[i].x].end();
						pas = *--it;
					}
					segment::change(1,Right,1,option[i].x,pas);
				}
				break;
			}
		}
	}
}
int main() {
	pre::init();
	pre::hash();
	solve();
	return 0;
}
posted @ 2019-10-04 17:26  resftlmuttmotw  阅读(665)  评论(0编辑  收藏  举报