线段树上二分

news/2025/3/2 16:41:08/文章来源:https://www.cnblogs.com/dinosaur7/p/18746406

别样的线段树

D. Points

原题链接:https://codeforces.com/problemset/problem/19/D

开始思路:

看到题目后有一个想法,先将所有坐标进行离散化,在横坐标方向上建立线段树,每个节点维护一个 \(set\) 即对应区间 \(l\) ~ \(r\)\(y\) 轴上的坐标,然后每次增删都可以在 \(O(log^2(n))\) 内完成,然后查询时,对区间进行直接二分,然后每次将对应区间的集合合并后取出,每次有效性检验检查是否存在大于当前查询的 \(y\),直接二分时间复杂度为 \(O(log(n))\),每次取出时间复杂度最坏情况下可 \(O(n)\),每次 \(upper\) _ \(bound\) 查询为 \(O(log(n))\),三者相乘,不出意外直接 \(tle\)

\(tle\)代码:

#include<bits/stdc++.h>
using namespace std;    typedef long long ll;
typedef pair<int,int> PII;const int N=2e5+10,mod=998244353;int n,m;vector<int> all,query;
vector<PII> points;struct Node{int l,r;set<int> ys;
}tr[4*N];void build(int u,int l,int r){if(l==r) tr[u]={l,r};else{tr[u]={l,r};int mid=l+r>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);}
}void add(int u,int x,int y){if(tr[u].l==x&&tr[u].r==x) tr[u].ys.insert(y);else{int mid=tr[u].l+tr[u].r>>1;if(x<=mid) add(u<<1,x,y);else add(u<<1|1,x,y);tr[u].ys.insert(y);}
}void rme(int u,int x,int y){if(tr[u].l==x&&tr[u].r==x) tr[u].ys.erase(y);else{int mid=tr[u].l+tr[u].r>>1;if(x<=mid) rme(u<<1,x,y);else rme(u<<1|1,x,y);tr[u].ys.erase(y);}
}set<int> get(int u,int l,int r){if(tr[u].l>=l&&tr[u].r<=r) return tr[u].ys;else{int mid=tr[u].l+tr[u].r>>1;if(r<=mid) return get(u<<1,l,r);else if(l>mid) return get(u<<1|1,l,r);else{set<int> left,right;left=get(u<<1,l,r),right=get(u<<1|1,l,r);if(left.size()<=right.size()) swap(left,right);for(auto y:right) left.insert(y);return left;}}
}inline bool check(int L,int R,int mi){set<int> temp=get(1,L,R);return temp.upper_bound(mi)!=temp.end();
}PII find(int x,int y){int l=x+1,r=all.size();while(l<r){int mid=l+r>>1;if(check(l,mid,y))r=mid;else l=mid+1;  }if(l>r||!check(l,l,y)) return {-1,-1};set<int> temp=get(1,l,l);int tx=l,ty=*temp.upper_bound(y);return {all[tx-1],all[ty-1]};
}void solve(){cin>>n;query=vector<int>(n);points=vector<PII>(n);string s;int x,y;for(int i=0;i<n;i++){cin>>s>>x>>y;query[i]=(s=="add")?1:(s=="remove")?2:3;    points[i]={x,y};all.push_back(x),all.push_back(y);}sort(all.begin(),all.end());all.erase(unique(all.begin(),all.end()),all.end());build(1,1,all.size());for(int i=0;i<n;i++){auto [x,y]=points[i];x=lower_bound(all.begin(),all.end(),x)-all.begin()+1;y=lower_bound(all.begin(),all.end(),y)-all.begin()+1;if(query[i]==1) add(1,x,y);else if(query[i]==2) rme(1,x,y);else{PII t=find(x,y);if(t.first!=-1) cout<<t.first<<' '<<t.second<<'\n';else cout<<-1<<endl;}}
}int main() {cin.tie(0)->sync_with_stdio(false);cout.tie(0);int t=1;// cin>>t;while(t--)solve();
}

正确做法:线段树上二分

这题确实应该用 \(set\),但应该是直接存储对应 \(x\) 坐标上的 \(y\)坐标,而线段树应该维护对应区间下 \(y\) 方向上坐标的最大值。每次插入,先将对应 \(y\) 坐标插入到对应 \(x\) 坐标下的 \(set\) 中,然后再去看对应坐标下存储的最大值是否改变,然后再去对线段树进行修改;对于删除也是上面的思路。而查询则要用线段树二分,固定查询区间 \(l\)~\(r\)找到是否存在第一个存储大于 \(y\) 的横坐标,每次只去找与查询区间有交集的位置,并且在左边符合条件的情况下,优先查询左边。

代码:

#include<bits/stdc++.h>
using namespace std;    typedef long long ll;
typedef pair<int,int> PII;const int N=4e5+10,mod=998244353;int n,m;vector<int> all,query;
vector<PII> points;struct Node{int l,r;int lmy;
}tr[4*N];set<int> colx[N];void build(int u,int l,int r){if(l==r) tr[u]={l,r,-1};else{tr[u]={l,r,-1};int mid=l+r>>1;build(u<<1,l,mid),build(u<<1|1,mid+1,r);}
}void add(int u,int x,int y){if(tr[u].l==x&&tr[u].r==x) tr[u].lmy=y;else{int mid=tr[u].l+tr[u].r>>1;if(x<=mid) add(u<<1,x,y);else add(u<<1|1,x,y);tr[u].lmy=max(tr[u<<1].lmy,tr[u<<1|1].lmy);}
}int find(int u,int l,int low){  // 线段树上二分查询if(l>all.size()) return -1;if(tr[u].l==tr[u].r){if(tr[u].lmy>low) return tr[u].l;return -1;}int mid=tr[u].l+tr[u].r>>1,res=-1;if(l<=mid&&tr[u<<1].lmy>low) res=find(u<<1,l,low);if(res!=-1) return res;if(tr[u<<1|1].lmy>low) return find(u<<1|1,l,low);return -1;
}       void solve(){cin>>n;query=vector<int>(n);points=vector<PII>(n);string s;int x,y;for(int i=0;i<n;i++){cin>>s>>x>>y;query[i]=(s=="add")?1:(s=="remove")?2:3;    points[i]={x,y};all.push_back(x),all.push_back(y);}sort(all.begin(),all.end());all.erase(unique(all.begin(),all.end()),all.end());build(1,1,all.size());unordered_map<int,int> mp;for(int i=0;i<all.size();i++) mp[all[i]]=i+1;for(int i=0;i<n;i++){auto [x,y]=points[i];x=mp[x], y=mp[y];if(query[i]==1){if(colx[x].empty()||*(--colx[x].end())<y) add(1,x,y);colx[x].insert(y);}if(query[i]==2){colx[x].erase(y);if(colx[x].empty()) add(1,x,-1);else add(1,x,*(--colx[x].end()));}if(query[i]==3){int t=find(1,x+1,y);if(t==-1) cout<<-1<<endl;else{int tx=t, ty=*colx[tx].upper_bound(y);cout<<all[tx-1]<<' '<<all[ty-1]<<endl;}}}
}int main() {cin.tie(0)->sync_with_stdio(false);cout.tie(0);int t=1;// cin>>t;while(t--)solve();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/892279.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GVIM环境配置记录

背景 GVIM只用自带安装的软件,可以完成文本编辑的功能,不过很多开发者编写了很多插件,配合这些插件来做文字代码编辑,能很好地提高速度与效率 GVIM的配置环境,装机后只配置一次,很容易忘记,下次重装系统或者配新机时,又要重新查找相关资料 这里做一次记录,方便后续重新…

VIM环境配置记录

背景 VIM只用自带安装的软件,可以完成文本编辑的功能,不过很多开发者编写了很多插件,配合这些插件来做文字代码编辑,能很好地提高速度与效率 VIM的配置环境,装机后只配置一次,很容易忘记,下次重装系统或者配新机时,又要重新查找相关资料 这里做一次记录,方便后续重新配…

endnote 基础使用

因为工作的原因,综述格式统一使用endnote。一小时速成,可以满足基础的综述引用。 安装 安装的是endnote 21,这个界面感觉比较简洁。 EndNote 21 的安装包中有汉化补丁 "D:\Program Files (x86)\EndNote 21\安装包\Crack\汉化补丁.exe" 2024-11-18 Endnote21更新-…

Day1-Markdown学习笔记

MarkDown学习 标题 一个#然后空格加内容是一级标题,两个是二级标题,三个是三级标题 字体 hello world hello world *** hello world*** hello world 一对*加上内容是斜体,两对是加粗,三对是既加粗又斜体, 一对~是加横线,就是废弃的意思 引用引用别人的内容一个> 加…

2025-03-02 闲话

2025-03-02 闲话走进这里时,求是园欢迎您。离开时,它会说,再见。昨天十一点,从这里走上蒋墩路,过了马路,是夜晚的学军。周末不再灯火通明,只剩下校门口的主灯,照亮着蓝色匾额的威严。昨天发了点牢骚,2023 年时进这个校园,不需要预约,刷脸时能发现不只一个我在这里的…

Semantic Kernel:接入azure中的deepseek-r1

SemanticKernel已经支持deepseek-r1了,官方的Blog地址是https://devblogs.microsoft.com/semantic-kernel/using-deepseek-models-in-semantic-kernel,同时给出了接入的Demo,遗憾的是deepseek不支持API的充值,没有办法测试。办法总比困难多,正好微软现在支持在Azure部署de…

C#中的Channel

在 .NET 的异步编程中,System.Threading.Channels 提供了一种强大的方式来处理生产者-消费者模式,尤其是当我们要在不同的任务或服务之间传递数据时。这篇文章我们就来聊聊 UnboundedChannelOptions 和 BoundedChannelOptions 这两个类,以及它们的使用场景和区别。代码背景介…

Kimi/DeepSeek最新论文MoBA与NSA阅读

From:https://www.big-yellow-j.top/posts/2025/02/21/Kimi-DS-Paper.html DeepSeek最新论文:Native Sparse Attention: Hardware-Aligned and Natively Trainable Sparse Attention以及 Kimi最新论文MOBA: MIXTURE OF BLOCK ATTENTION FOR LONG-CONTEXT LLMS这几篇文章都是针…

Windows 剪贴板 编程原理引入

前言 不得不说上三休四的生活就是舒服,我都有精力提升自己了。 本文将基于自己在生活中遇到的现象进行探索,因此问题引入对自己较为重要,读者可以跳过。 文章主要探讨剪贴板格式问题,即下面的链接。https://learn.microsoft.com/zh-cn/windows/win32/dataxchg/standard-cli…

.NET9中基于策略角色验证的包冲突

今天在.NET项目中,使用基于策略角色的鉴权时,遇到一个401的问题,场景如下:Program.cs代码如下:using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; usin…

yolov5预处理

1.yolov5预处理流程1.等比缩放与填充:将输入图像等比缩放到目标尺寸(如640640),并在多余部分填充灰条,保持图像的宽高比不变。这一过程也被称为“letterbox”。  2.颜色空间转换:将图像从BGR格式转换为RGB格式(OpenCV默认读取为BGR)。  3.归一化:将像素值从[0, 25…