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

题目大意:

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

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

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

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

#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;
}
posted @ 2022-11-07 16:39  腾云今天首飞了吗  阅读(42)  评论(0编辑  收藏  举报