To_Heart—题解——CF762E

题意

在一维坐标轴上分布了 n n n 个机器人,第个机器人的坐标是 x i x_i xi

i i i 个机器人的视野是 r i r_i ri 。也就是说,第 i i i 个机器人能看到 [ x i − r i , x i + r i ] [x_i-r_i,x_i+r_i] [xiri,xi+ri] 范围内的所有其他机器人。

i i i 个机器人的智商是 f i f_i fi 如果有一对机器人可以互相看到,并且它们的智商相差不超过 k k k ,那么它们就可以互相交流。

你需要求出来有多少对机器人可以互相交流。

题解

i i i , j j j 满足 1 ≤ i ≤ j ≤ n 1\leq i \leq j \leq n 1ijn

有两个显然的式子:
∣ x i − x j ∣ ≤ min ⁡ ( r i , r j ) |x_i-x_j|\leq \min(r_i,r_j) xixjmin(ri,rj)
∣ f i − f j ∣ ≤ k |f_i-f_j|\leq k fifjk

我们先忽略掉第二个式子

然后发现第一个式子有 min ⁡ \min min 操作,挺不好看的,考虑以 r r r 为关键字从大到小排序后,化简得:

∣ x i − x j ∣ ≤ r j |x_i-x_j|\leq r_j xixjrj

发现绝对值也挺不好看的,所以去掉绝对值:

x i − x j ≤ r j x_i-x_j\leq r_j xixjrj
x j − x i ≤ r j x_j-x_i\leq r_j xjxirj

转换得到:

x i − r i ≤ x j ≤ x i + r i x_i-r_i\leq x_j\leq x_i+r_i xirixjxi+ri

所以答案就转换为了对于每一个 i i i ,满足 x i − r i ≤ x j ≤ x i + r i x_i-r_i\leq x_j\leq x_i+r_i xirixjxi+ri j j j 的个数和,这个很好用线段树维护。

这时我们再来看看第二个式子:

∣ f i − f j ∣ ≤ k |f_i-f_j|\leq k fifjk

同样可以转换为:

f i − k ≤ f j ≤ f i + k f_i-k\leq f_j \leq f_i+k fikfjfi+k

再看看 f f f 的范围为 1 0 4 10^4 104,所以我们直接开 1 0 4 10^4 104个线段树,然后再分别维护每个线段树就行了。

代码

因为我们考试的时候f的数据范围较大,用了离散化,但是不影响过题

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define int ll 

int lsh[200005];
int len=0;

struct ss{
	int x,r,q;
}a[200005];
bool cmp(ss x,ss y){
	if(x.r==y.r)
		return x.x<y.x;
	return x.r>y.r;
}
int n,k;

struct zz{
	int l,r;
	int val;
};

int tot=0;
zz t[4000005];
int rt[200005];
int New_Node(){
	tot++;
	t[tot].l=t[tot].r=t[tot].val=0;
	return tot;
}	
struct Tree{
	#define lc t[p].l
	#define rc t[p].r
	void Push_up(int p){
		int now=0;
		if(lc)	now+=t[lc].val;
		if(rc) 	now+=t[rc].val;
		t[p].val=now;
	}
	void Change_Tree(int p,int l,int r,int x){
		if(l==r){
			t[p].val++;
			return ;
		}
		int mid=(l+r)>>1;
		if(x<=mid){
			if(!lc)	lc=New_Node();
			Change_Tree(lc,l,mid,x);
		}
		else{
			if(!rc)	rc=New_Node();
			Change_Tree(rc,mid+1,r,x);
		}
		Push_up(p);
	}
	int Find_Tree(int p,int l,int r,int L,int R){
		if(L<=l&&r<=R){
			return t[p].val;
		}
		int mid=(l+r)>>1;
		int now=0;
		if(L<=mid&&lc)
			now+=Find_Tree(lc,l,mid,L,R);
		if(mid+1<=R&&rc)
			now+=Find_Tree(rc,mid+1,r,L,R);
		return now;
	} 
}T[200005];

signed main(){
	int Max=0,Min=0x3f3f3f3f;
	cin>>n>>k;
	for(int i=1;i<=n;i++){
		scanf("%lld%lld%lld",&a[i].x,&a[i].r,&a[i].q);
		lsh[++len]=a[i].q;
		Max=max(Max,a[i].x+a[i].r);
		Min=min(Min,a[i].x-a[i].r);
	}	
	sort(a+1,a+n+1,cmp);
	sort(lsh+1,lsh+len+1);
	len=unique(lsh+1,lsh+len+1)-(lsh+1);
	for(int i=1;i<=n;i++)
		rt[i]=New_Node();
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=a[i].q-k;j<=a[i].q+k;j++){
			int now=lower_bound(lsh+1,lsh+len+1,j)-lsh;
			if(lsh[now]!=j)
				continue;
			ans+=T[now].Find_Tree(rt[now],Min,Max,a[i].x-a[i].r,a[i].x+a[i].r);
		}
		int now=lower_bound(lsh+1,lsh+len+1,a[i].q)-lsh;
		T[now].Change_Tree(rt[now],Min,Max,a[i].x); 
	}
	cout<<ans<<endl;
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值