\(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;
}