HDU 6534 Chika and Friendly Pairs(树状数组+莫队)

给一串数字,叫你算L到R之间绝对值为k的数对有多少,线段树不行,亲手超的时,得用树状数组而且还要优化一下离散化。

a[]数组是原本数列的数组,b是a经过排序去重后得到的数组,树状数组c[i]存的是小于等于b[i]的数出现的次数,莫队的时候区间扩大时,被扩充进来的数 x 对于答案的贡献就是当前区间内,所有在 x-k 到 x+k 之间数字出现的次数,直接query(上界)- query(下界)就是对答案的贡献,上下界 up 和 low 数组 表示 第 i 个数其 b[i]+k 和 b[i]-k-1 在 b 数组中的位置,如果这里每次都用二分查找其位置会超时,所以在这里用两个数组把他们存起来

//#if 0
#include <map>
#include <stack>
#include <queue>
#include <vector>
#include <math.h>
#include <string>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <algorithm>
#include <time.h>
#include <set>
#include <list>
#include <iostream>
#define ll long long
#define ull unsigned long long
#define cls(x) memset(x,0,sizeof(x))
#define clslow(x) memset(x,-1,sizeof(x))
#define INF  0x3f3f3f3f
//typedef long long ll;
using namespace std;
const int mod=1e9+7;
const int INF=1e9+7;
const int maxn=27000+50;

struct Mo
{
    int l,r,id;
}q[maxn];

int n,m,k,block,len;
int a[maxn],b[maxn],c[maxn],ans[maxn],up[maxn],low[maxn];

int sear(int x)
{
    int l=1,r=len-1;
    while(l<=r){
        int mid=(l+r)>>1;
        if(b[mid]==x)return mid;
        if(b[mid]<x)l=mid+1;
        if(b[mid]>x)r=mid;
    }
}

int cmp(Mo a,Mo b)
{
    if(a.l/block==b.l/block)return a.r<b.r;
    return a.l<b.l;
}

int lowbit(int x)
{
    return x&(-x);
}

void Update(int x,int C)
{
    for(int i=x;i<=maxn;i+=lowbit(i)){
        c[i]+=C;
    }
}

int Query(int x)
{
    int ANS=0;
    for(int i=x;i>0;i-=lowbit(i)){
        ANS+=c[i];
    }
    return ANS;
}

int main()
{
//    freopen("in.txt","r",stdin);
    scanf("%d%d%d",&n,&m,&k);
    block=sqrt(n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i];
    sort(b+1,b+1+n);
    len=unique(b+1,b+1+n)-b;

    for(int i=1;i<=n;i++){
        int x=lower_bound(b+1,b+len,a[i]-k)-b;
        int y=upper_bound(b+1,b+len,a[i]+k)-b;
        low[i]=x-1;
        up[i]=y-1;
//        cout<<a[i]<<" "<<x<<" "<<y-1<<endl;
    }

    for(int i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    sort(q+1,q+1+m,cmp);

    int ret=0;
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        while(r<q[i].r){
            ret+=Query(up[r+1])-Query(low[r+1]);//应该排除自身对答案的影响,所以再其未加入之时
//便先行计算,至于区间减少时应该先删掉自身再计算。
            Update(sear(a[r+1]),1);
            r++;
        }

        while(r>q[i].r){
            Update(sear(a[r]),-1);
            ret-=Query(up[r])-Query(low[r]);
            r--;
        }

        while(l<q[i].l){
//            cout<<" asdf "<<endl;
            Update(sear(a[l]),-1);
            ret-=Query(up[l])-Query(low[l]);
            l++;
        }

        while(l>q[i].l){
            ret+=Query(up[l-1])-Query(low[l-1]);
            Update(sear(a[l-1]),1);
            l--;
        }
        ans[q[i].id]=ret;
    }
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值