• 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
    ");
    				else {
    					int j = segment::findans(1,Right,1,option[i].x,option[i].y);
    					printf("%d %d
    ",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
    ");
    				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
    ",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;
    }
    
  • 相关阅读:
    CSS样式表
    lianxi!
    传值
    lei!
    3.10
    if else&& stwitch break
    if else 语句
    2016.3.6
    进制转换
    PHP 面向对象的三大特征
  • 原文地址:https://www.cnblogs.com/resftlmuttmotw/p/11622607.html
Copyright © 2020-2023  润新知