传送门
题意:
给你一个数组,对于第i个数来说,如果存在一个位置j,使得j>i并且a[j]-k<=a[i]<=a[j]+k,那么这对数就称为好的,有q个询问,问你l到r区间有多少对好的数。
思路:
这题卡常数卡的好厉害呀,比赛时是用主席树+莫队写的gg,主席数常数太大了,用树状数组和莫队维护,同时先把a[i],a[i]-k,a[i]+k先全部离散好了。
#include<bits/stdc++.h>
#define il inline
#define pb push_back
#define fi first
#define se second
#define ms(_data,v) memset(_data,v,sizeof(_data))
#define sc(n) scanf("%d",&n)
#define SC(n,m) scanf("%d %d",&n,&m)
#define SZ(a) int((a).size())
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define drep(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
typedef long long ll;
const ll inf=0x3f3f3f3f;
const double PI=acos(-1.0);
const double eps=1e-9;
const int maxn=3e4+5;
int sum[maxn<<2],n,m,k;
int a[maxn],b[maxn<<2];
//树状数组
il void add(int p,int x) {
while(p<=3*n+10) sum[p]+=x,p+=p&-p;
}
il int ask(int p) {
int res=0;
while(p) res+=sum[p],p-=p&-p;
return res;
}
il int r_ask(int l,int r) {
return ask(r)-ask(l-1);
}
//莫队
struct node {
int id,l,r;
} qu[maxn];
int block,res=0,ans[maxn];
il bool cmp(node x,node y) {
if(x.l/block != y.l/block) return x.l<y.l;
else return x.r<y.r;
}
int ida[maxn],upa[maxn],doa[maxn];
int main() {
std::ios::sync_with_stdio(0);
sc(n),sc(m),sc(k);
int cnt=0;
rep(i,1,n) {
sc(a[i]);
b[++cnt]=a[i],b[++cnt]=a[i]-k,b[++cnt]=a[i]+k;
}
sort(b+1,b+cnt+1);
int sz=unique(b+1,b+cnt+1)-(b+1);
rep(i,1,n) {
ida[i]=lower_bound(b+1,b+sz+1,a[i])-b;
upa[i]=lower_bound(b+1,b+sz+1,a[i]+k)-b;
doa[i]=lower_bound(b+1,b+sz+1,a[i]-k)-b;
}
rep(i,1,m) SC(qu[i].l,qu[i].r),qu[i].id=i;
block=sqrt(n);
sort(qu+1,qu+m+1,cmp);
int L=qu[1].l,R=qu[1].l-1;
rep(i,1,m) {
while(L<qu[i].l) {
add(ida[L],-1);
res-=r_ask(doa[L],upa[L]);
L++;
}
while(L>qu[i].l) {
L--;
res+=r_ask(doa[L],upa[L]);
add(ida[L],1);
}
while(R>qu[i].r) {
add(ida[R],-1);
res-=r_ask(doa[R],upa[R]);
R--;
}
while(R<qu[i].r) {
R++;
res+=r_ask(doa[R],upa[R]);
add(ida[R],1);
}
ans[qu[i].id]=res;
}
rep(i,1,m) printf("%d\n",ans[i]);
return 0;
}