题意:
给定一个长度为n的数组,有m个查询,每次查询区间【l,r】中满足abs(Ai - Aj) <= K 的(i,j)的数量
分析:
由于查询可以离线,容易想到莫队,考虑怎么维护当前区间满足条件的(i,j)的对数,对于数值Ai的贡献:
(1)Ai > Aj,满足条件时有:Ai - Aj <= K,即:Aj >= Ai - K
(2)Ai < Aj,满足条件时有:Aj - Ai <= K,即:Aj <= Ai + K
所以Ai对【l,r】的贡献为区间中值域为【Ai-K,Ai+K】的个数,转换为:区间中小于等于Ai+K的个数 减去 小于Ai-K的个数,正好可以用树状数组计算并维护,由于数值太大,要先离散化
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn = 1e5+15;
int a[maxn],b[maxn],c[maxn],n,m,k,len,block;
struct segement{
int l,r,id;
}s[maxn];
bool cmp(segement a,segement b){
return a.r/block == b.r/block ? a.l < b.l : a.r < b.r;
}
LL res,tr[maxn],ans[maxn];
inline int lowbit(int x){
return x&(-x);
}
void updata(int pos,int val){
while(pos < len){
tr[pos] += val;
pos += lowbit(pos);
}
}
LL query(int pos){
LL res = 0;
while(pos > 0){
res += tr[pos];
pos -= lowbit(pos);
}
return res;
}
inline void add(int x){
int pos = lower_bound(c+1,c+len,a[x]+k) - c;
if(c[pos] > a[x]+k) pos--;
int q1 = query(pos);
pos = lower_bound(c+1,c+len,a[x]-k) - c - 1;
int q2 = query(pos);
res += q1 - q2;
updata(b[x],1);
}
inline void del(int x){
int pos = lower_bound(c+1,c+len,a[x]+k) - c;
if(c[pos] > a[x]+k) pos--;
int q1 = query(pos);
pos = lower_bound(c+1,c+len,a[x]-k) - c - 1;
int q2 = query(pos);
res = res - (q1 - q2) + 1; //a[x]肯定在条件范围内,所以多减了要加回来
updata(b[x],-1);
}
int main(){
scanf("%d %d %d",&n,&m,&k);
for(int i = 1;i <= n; ++i) {
scanf("%d",a+i);
c[i] = a[i];
}
for(int i = 0;i < m; ++i){
scanf("%d %d",&s[i].l,&s[i].r);
s[i].id = i;
}
block = sqrt(n*1.0);
sort(s,s+m,cmp);
sort(c+1,c+n+1);
len = unique(c+1,c+n+1) - c;
c[len] = 2e9;
for(int i = 1;i <= n; ++i) b[i] = lower_bound(c+1,c+len,a[i]) - c; //离散化
int L = 1, R = 0;
for(int i = 0;i < m; ++i){
while(R < s[i].r) ++R,add(R);
while(R > s[i].r) del(R),--R;
while(L < s[i].l) del(L),++L;
while(L > s[i].l) --L,add(L);
ans[s[i].id] = res;
}
for(int i = 0;i < m; ++i) printf("%lld\n",ans[i]);
return 0;
}