First of all
stO LH Orz
题意
在一维坐标轴上分布了 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] [xi−ri,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 1≤i≤j≤n
有两个显然的式子:
{
∣
x
i
−
x
j
∣
≤
min
(
r
i
,
r
j
)
∣
f
i
−
f
j
∣
≤
k
\begin{cases} |x_i-x_j|\leq \min(r_i,r_j) \\ |f_i-f_j|\leq k \end{cases}
{∣xi−xj∣≤min(ri,rj)∣fi−fj∣≤k
我们先忽略掉第二个式子
然后发现第一个式子有
min
\min
min 操作,挺不好看的,考虑以
r
r
r 为关键字从大到小排序后,化简得:
∣ x i − x j ∣ ≤ r j |x_i-x_j|\leq r_j ∣xi−xj∣≤rj
发现绝对值也挺不好看的,所以去掉绝对值:
{ x i − x j ≤ r j x j − x i ≤ r j \begin{cases} x_i-x_j\leq r_j \\ x_j-x_i\leq r_j \end{cases} {xi−xj≤rjxj−xi≤rj
转换得到:
x i − r i ≤ x j ≤ x i + r i x_i-r_i\leq x_j\leq x_i+r_i xi−ri≤xj≤xi+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 xi−ri≤xj≤xi+ri 的 j j j 的个数和,这个很好用线段树维护。
这时我们再来看看第二个式子:
∣ f i − f j ∣ ≤ k |f_i-f_j|\leq k ∣fi−fj∣≤k
同样可以转换为:
f i − k ≤ f j ≤ f i + k f_i-k\leq f_j \leq f_i+k fi−k≤fj≤fi+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;
}