To_Heart—题解——CF762E
First of all
stO LH Orz
题意
在一维坐标轴上分布了 \(n\) 个机器人,第个机器人的坐标是 \(x_i\) 。
第 \(i\) 个机器人的视野是 \(r_i\) 。也就是说,第 \(i\) 个机器人能看到 \([x_i-r_i,x_i+r_i]\) 范围内的所有其他机器人。
第 \(i\) 个机器人的智商是 \(f_i\) 如果有一对机器人可以互相看到,并且它们的智商相差不超过 \(k\) ,那么它们就可以互相交流。
你需要求出来有多少对机器人可以互相交流。
题解
设 \(i\) , \(j\) 满足 \(1\leq i \leq j \leq n\)
有两个显然的式子:
\[\begin{cases} |x_i-x_j|\leq \min(r_i,r_j) \\ |f_i-f_j|\leq k \end{cases}
\]
我们先忽略掉第二个式子
然后发现第一个式子有 \(\min\) 操作,挺不好看的,考虑以 \(r\) 为关键字从大到小排序后,化简得:
\[|x_i-x_j|\leq r_j
\]
发现绝对值也挺不好看的,所以去掉绝对值:
\[\begin{cases} x_i-x_j\leq r_j \\ x_j-x_i\leq r_j \end{cases}
\]
转换得到:
\[x_i-r_i\leq x_j\leq x_i+r_i
\]
所以答案就转换为了对于每一个 \(i\) ,满足 \(x_i-r_i\leq x_j\leq x_i+r_i\) 的 \(j\) 的个数和,这个很好用线段树维护。
这时我们再来看看第二个式子:
\[|f_i-f_j|\leq k
\]
同样可以转换为:
\[f_i-k\leq f_j \leq f_i+k
\]
再看看 \(f\) 的范围为\(10^4\),所以我们直接开\(10^4\)个线段树,然后再分别维护每个线段树就行了。
代码
因为我们考试的时候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;
}