CF1045G AI Robots (动态开点线段树 + 离散化)(关于其空间复杂度的分析)

题目大意:

火星上有 N 个机器人排成一行,第 i 个机器人的位置为 xi,视野维 ri,智商为 qi。我们认为第 i 个机器人可以看到的位置是 [xiri,xi+ri]。如果一对机器人可以相互看见,且他们的智商 qi 的差距不大于 k,那么他们会开始聊天。为了防止他们炒起来,请计算有多少机器人可以聊天。

题目中出现了两个制约答案的条件:相互看见,智商差距小于 k。那么在一棵线段树种存储这两维的信息?显然是难以维护和查找的。考虑到颜色种类不多,我们可以对智商离散化,对每一种智商建一棵线段树,使我们能够查询某个区间内该智商的机器人总数。当然,我们还要对坐标进行离散化。注意,在离散化的过程中,除了将机器人的位置 r[i].x 加入离散化的动态数组,还要将 r[i].x+r[i].rr[i].xr[i].r 加入该动态数组,这样才能便于我们更好地确定每个机器人的视野内有多少个机器人。

除了记录答案的部分,大体思路如上。但这里有一个问题,如果我们按照传统线段树,开成tree[][4MAXN] 这样的话空间显然会炸,那么就只能用动态开点线段树来解决。先确定最多会用到多少个结点。对于每个结点,记录其左儿子和右儿子的编号即可。像对于每一种智商开的新的线段树,用一个 root 数组记录它的根节点的编号,就可以实现动态开点了。

现在考虑最后一个问题:怎么统计答案?假如一个视野小的机器人能看见一个视野大的机器人,那么这对机器人肯定能够相互看见。然后枚举一个智商 j,使其与当前机器人的智商的差距不大于 k,将智商为 j 且在 [xiri,xi+ri] 范围内的机器人的总数加起来,就是当前机器人能够吵架的机器人的总数。

#include<bits/stdc++.h> #define mid (tl + tr) / 2 #define int long long using namespace std; int n,k; const int N = 1e5 + 10; const int MAXN = N * 10; struct robot{ int x,r,q; }r[MAXN + 25]; int root[MAXN + 25],tot; struct node{ int ls,rs,num; }tree[MAXN + 225]; vector<int> col; int ans; void change(int i,int tl,int tr,int pos){ if(tl == tr){ tree[i].num++; return; } if(pos <= mid){ if(tree[i].ls == 0)tree[i].ls = ++tot; change(tree[i].ls,tl,mid,pos); } else { if(tree[i].rs == 0)tree[i].rs = ++tot; change(tree[i].rs,mid + 1,tr,pos); } tree[i].num = tree[tree[i].ls].num + tree[tree[i].rs].num; } int query(int i,int tl,int tr,int l,int r){ if(tl > r || tr < l)return 0; if(tl >= l && tr <= r)return tree[i].num; int ss = 0; if(tree[i].ls != 0)ss += query(tree[i].ls,tl,mid,l,r); if(tree[i].rs != 0)ss += query(tree[i].rs,mid + 1,tr,l,r); return ss; } bool cmp(robot a,robot b){ return a.r > b.r; } vector<int> pos; signed main(){ ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n >> k; int mx = 0; for(int i = 1; i <= n; i++){ cin >> r[i].x >> r[i].r >> r[i].q; mx = max(mx,r[i].x); col.push_back(r[i].q); pos.push_back(r[i].x + r[i].r); pos.push_back(r[i].x - r[i].r); pos.push_back(r[i].x); } sort(col.begin(),col.end()); col.erase(unique(col.begin(),col.end()),col.end()); for(int i = 0; i < col.size(); i++){ root[i] = ++tot; } sort(pos.begin(),pos.end()); pos.erase(unique(pos.begin(),pos.end()),pos.end()); sort(r + 1,r + 1 + n,cmp); for(int i = 1; i <= n; i++){ int now = lower_bound(col.begin(),col.end(),r[i].q) - col.begin(); int l = lower_bound(pos.begin(),pos.end(),r[i].x - r[i].r) - pos.begin() + 1; int r1 = lower_bound(pos.begin(),pos.end(),r[i].x + r[i].r) - pos.begin() + 1; int o = lower_bound(pos.begin(),pos.end(),r[i].x) - pos.begin() + 1; for(int j = now + 1; j < col.size() && col[j] - r[i].q <= k; j++){ ans += query(root[j],1,pos.size(),l,r1); } for(int j = now; j >= 0 && r[i].q - col[j] <= k; j--){ ans += query(root[j],1,pos.size(),l,r1); } change(root[now],1,pos.size(),o); } cout << ans; }

__EOF__

本文作者Never Gonna Give You Up!
本文链接https://www.cnblogs.com/CZ-9/p/16866482.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   腾云今天首飞了吗  阅读(44)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示