51nod 1290 Counting Diff Pairs(莫队+树状数组)

51nod 1290 Counting Diff Pairs

问题描述

一个长度为N的正整数数组A,给出一个数K以及Q个查询,每个查询包含2个数l和r,对于每个查询输出从A[i]到A[j]中,有多少对数,abs(A[i] - A[j]) <= K(abs表示绝对值)。

Input

第1行:3个数N,K,Q,中间用空格分隔,N为数组A的长度,K为差距,Q为查询的数量。(2 <= N <= 50000, 0 <= K <= 10^9, 1 <= Q <= 50000)
第2至N + 1行:每行1个数,对应数组中的数(1 <= A[i] <= 10^9)
第N + 2至N + M + 1行:每行2个数l, r中间用空格分隔(0 <= l <= r < N)

Output

输出共Q行,对于Q条查询的结果。

Input示例

5 2 3
1
3
4
3
0
0 1
1 3
0 4

Output示例

1
3
6

solution:

假设已经求出了(l,r)的结果,我们能不能快速得求出相邻的询问即(l+1,r)、(l-1,r)、(l,r+1)、(l,r-1)?显然是可以的。如果要加入一个值,对答案的影响是加上(l,r)中与 ar+1 差小于等于k的值的个数,能不能维护这个操作?树状数组完全可以胜任。单点插入、删除时间复杂度O(lg n),更新答案的时间复杂度也是O(lg n)。
满足了这个性质,就可以使用莫队算法了。
讲询问区间看成一个在平面直角坐标系上的点,为了求出所有的询问,最优的做法是求出最小曼哈顿生成树,可是好麻烦……,我们可以把平面根据x坐标分块,分块大法好,对于同一块中的点,根据y坐标排序,然后对于同一块中的点,从下向上依次移动,对于前一块的最后一个点和下一块的第一个点,暴力解决。
我们来计算一下时间复杂度,对于同一块中的移动,次数O(n),每次O( n ),所以是O(n n ).再来算跨块的移动,次数O( n ),每次O(n),所以也是(n n )。再加上树状数组的O(lg n),所以总的时间复杂度是O(n n lg n)。
对了,这道题要离散化

code:

#include<iostream>
#include<stdio.h>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<cstring>
using namespace std;
struct node{
    int x,y,z;
}ques[50005];
bool cmp1(node x,node y){
    return x.x<y.x||x.x==y.x&&x.y<y.y;
}
bool cmp2(node x,node y){
    return x.y<y.y||x.y==y.y&&x.x<y.x;
}
int n,k,q,bit[50005],a[50005],b[50005],cnt,num,belong[50005],l[50005],r[50005],nowx,nowy,Max[500005],Min[500005];
int lowbit(int x){
    return x&-x;
}
void add(int x,int y){
    while(x<=cnt){
        bit[x]+=y;
        x+=lowbit(x);
    }
}
int query(int x){
    int ans=0;
    while(x){
        ans+=bit[x];
        x-=lowbit(x);
    }
    return ans;
}
long long ans,aa[50005];
int main(){
    scanf("%d%d%d",&n,&k,&q);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        a[i]++;
        b[i]=a[i];
    }
    sort(b+1,b+1+n);
    cnt=unique(b+1,b+1+n)-b-1;
    for(int i=1;i<=n;i++){
        int tmp=a[i];
        a[i]=lower_bound(b+1,b+1+cnt,a[i])-b;
        if(!Min[a[i]])
            Min[a[i]]=lower_bound(b+1,b+1+cnt,tmp-k)-b;
        if(!Max[a[i]])
            Max[a[i]]=upper_bound(b+1,b+1+cnt,tmp+k)-b-1;
    }
    for(int i=1;i<=q;i++){
        scanf("%d%d",&ques[i].x,&ques[i].y);
        ques[i].x++;
        ques[i].y++;
        ques[i].z=i;
    }
    sort(ques+1,ques+1+q,cmp1);
    num=sqrt(n);
    for(int i=1;i<=q;i++){
        int x=(ques[i].x-1)/num+1;
        if(x>num)
            x=num;
        if(!l[x])
            l[x]=i;
        r[x]=i;
    }
    nowx=1;
    nowy=1;
    add(a[1],1);
    ans=0;
    for(int i=1;i<=num;i++){
        if(!l[i])
            continue;
        sort(ques+l[i],ques+r[i]+1,cmp2);
        for(int j=l[i];j<=r[i];j++){
            while(nowy<ques[j].y){
                ans+=(long long)query(min(cnt,Max[a[nowy+1]]))-query(max(0,Min[a[nowy+1]]-1));
                add(a[nowy+1],1);
                nowy++;
            }
            while(nowx>ques[j].x){
                ans+=(long long)query(min(cnt,Max[a[nowx-1]]))-query(max(0,Min[a[nowx-1]]-1));
                add(a[nowx-1],1);
                nowx--;
            }
            while(nowy>ques[j].y){
                add(a[nowy],-1);
                ans-=(long long)query(min(cnt,Max[a[nowy]]))-query(max(0,Min[a[nowy]]-1));
                nowy--;
            }
            while(nowx<ques[j].x){
                add(a[nowx],-1);
                ans-=(long long)query(min(cnt,Max[a[nowx]]))-query(max(0,Min[a[nowx]]-1));
                nowx++;
            }   
            aa[ques[j].z]=ans;
        }   
    }
    for(int i=1;i<=q;i++)
        printf("%lld\n",aa[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值