HDU-6534-(莫队+权值树状数组维护 大水题)

题目
在这里插入图片描述Sample Input
7 5 3
2 5 7 5 1 5 6
6 6
1 3
4 6
2 4
3 4

Sample Output
0
2
1
3
1

题意: 给一个大小为n的序列,和一个k。询问m个区间。问每个区间里可以找出多少个友对。
友对: i<j,| a[j]-a[i] |<k。
思路: a[j]-a[i]<k -----a[j]<a[i]+k a[i]-a[j]<k ---- a[i]-k<a[j]
则需要找到对于每个j满足 a[i]-k<a[j]<a[i]+k的个数。很简单就是树状数组维护权值+莫队。
记得离散化。离散化的值用数组先预处理存好。否则莫队每次修改时都要知道他的refl,要lower_bound会超时.

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define m(a,b) memset(a,b,sizeof a)
#define en '\n'
#define low(i) i&-i
using namespace std;
typedef long long ll;
const int N=1e5+5,M=1e5+5;
template<class T>void rd(T &x)
{
    x=0;int f=0;char ch=getchar();
    while(ch<'0'||ch>'9')  {f|=(ch=='-');ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
    x=f?-x:x;
    return;
}
ll a[N],refl[N*3],c[N*3];
int dex[N*3],tot,n;
struct Query{int id,l,r;}q[M];
ll ans[N],res;
int dexl,dexr,block;
bool cmp(Query x,Query y)
{
    if(x.l/block==y.l/block)
        return x.r<y.r;
    return x.l/block<y.l/block;
}
void modify(int i,int val)
{
    for(;i<=n*3;i+=low(i))
        c[i]+=val;
}
int getsum(int i)
{
    int ans=0;
    for(;i;i-=low(i))
        ans+=c[i];
    return ans;
}
void add(int x)
{
     int l=dex[x+n],r=dex[x+n*2],w=dex[x];
     res+=getsum(r)-getsum(l-1);
     modify(w,1);//先统计再modify.否则会多考虑a[x]-a[x]=0,这种不合理配对情况。(x是同一个位置,a[x]=a[y]这种不同位置的本来就要考虑,已经在统计中考虑过"a[x]"这个值了)
}
void del(int x)
{
     int l=dex[x+n],r=dex[x+n*2],w=dex[x];
     modify(w,-1);
     res-=getsum(r)-getsum(l-1);//同上,先统计的话会多减去a[x]-a[x]=0,这种不合理的情况.
}
int main()
{
    int m,k;rd(n),rd(m),rd(k);block=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        rd(a[i]),a[i+n]=a[i]-k,a[i+n*2]=a[i]+k;
        refl[i]=a[i],refl[i+n]=a[i]-k,refl[i+n*2]=a[i]+k;
    }
    sort(refl+1,refl+n*3+1);
    tot=unique(refl+1,refl+n*3+1)-(refl+1);
    for(int i=1;i<=n;i++)
    {
        dex[i]=lower_bound(refl+1,refl+tot+1,a[i])-refl;
        dex[i+n]=lower_bound(refl+1,refl+tot+1,a[i+n])-refl;
        dex[i+n*2]=lower_bound(refl+1,refl+tot+1,a[i+n*2])-refl;
    }
    for(int i=1;i<=m;i++)
        rd(q[i].l),rd(q[i].r),q[i].id=i;
    sort(q+1,q+m+1,cmp);
    dexl=1,dexr=0,res=0;
    for(int i=1;i<=m;i++)
    {
        while(dexr>q[i].r)
            del(dexr--);
        while(dexr<q[i].r)
            add(++dexr);
        while(dexl<q[i].l)
            del(dexl++);
        while(dexl>q[i].l)
            add(--dexl);
        ans[q[i].id]=res;
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值