2019CCPC湘潭邀请赛的C题
大小为n的数组,m次查询,查询区间[l,r]内大小差距不超过k的数对的个数,可离线。m范围[1,27000],n范围[1,27000]
这是一道分块+莫队+树状数组的题目
将使用分块降低莫队复杂度为
O
(
n
)
O(\sqrt n)
O(n),用莫队+树状数组维护当前区间的答案,树状数组维护的是当前区间内每个数的数量,增加区间每加入一个数x,结果增加当前区间内[x-k,x+k]的数的个数并在树状数组中这个数的数量加一;减小区间时每去掉一个数x,树状数组中这个数的数量减一,结果减少当前区间内[x-k,x+k]的数的个数。
写错的地方:
莫队初始状态:l=1,r=0,不是l=0,r=0
树状数组的下标为离散化后每个数的位置,第i个数a[i]的位置是mid[i],a[i]+k的位置是up[maxn],a[i]-k的位置是down[maxn]。
复杂度 O ( m n l o g ( n ) ) O(m\sqrt nlog(n)) O(mnlog(n))
#include<bits/stdc++.h>
#define lowbit(x) x&(-x)
#define de(x) cout<<#x<<" = "<<x<<endl;
using namespace std;
typedef long long ll;
const ll maxn=1e5+10;
ll Tree[maxn];//树状数组
ll a[maxn];
ll b[maxn];//离散化
ll be[maxn];//分块
ll up[maxn],mid[maxn],down[maxn];//a[i]-k,a[i],a[i]+k排序并离散化后在b中的左标
ll ans[maxn];
void update(ll x,ll val){
while(x<maxn){
Tree[x]+=val;
x+=lowbit(x);
}
}
ll query(ll x){
ll sum=0;
while(x>0){
sum+=Tree[x];
x-=lowbit(x);
}
return sum;
}
struct Q{
ll l,r,id;
friend bool operator <(Q x,Q y){
return be[x.l]==be[y.l]?x.r<y.r:x.l<y.l;
}
}q[maxn];
int main(){
ll n,m,k;
scanf("%lld%lld%lld",&n,&m,&k);
ll tot=0;
ll sz=sqrt(n);
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
b[++tot]=a[i]-k;
b[++tot]=a[i];
b[++tot]=a[i]+k;
be[i]=i/sz;
}
sort(b+1,b+1+tot);
ll num=unique(b+1,b+1+tot)-b-1;
for(ll i=1;i<=n;i++){
down[i]=lower_bound(b+1,b+1+num,a[i]-k)-b;
mid[i]=lower_bound(b+1,b+1+num,a[i])-b;
up[i]=lower_bound(b+1,b+1+num,a[i]+k)-b;
}
for(ll i=1;i<=m;i++){
scanf("%lld%lld",&q[i].l,&q[i].r);
q[i].id=i;
}
sort(q+1,q+1+m);
ll l=1,r=0,res=0;
for(ll i=1;i<=m;i++){
while(r<q[i].r){
r++;
res+=query(up[r])-query(down[r]-1);
update(mid[r],1);
}
while(r>q[i].r){
update(mid[r],-1);
res-=query(up[r])-query(down[r]-1);
r--;
}
while(l>q[i].l){
l--;
res+=query(up[l])-query(down[l]-1);
update(mid[l],1);
}
while(l<q[i].l){
update(mid[l],-1);
res-=query(up[l])-query(down[l]-1);
l++;
}
ans[q[i].id]=res;
}
for(ll i=1;i<=m;i++){
printf("%lld\n",ans[i]);
}
}