2019CCPC湖南全国邀请赛 C - Chika and Friendly Pairs(莫队+离散化+树状数组)

本文介绍了一种使用莫队算法、离散化与树状数组解决区间查询问题的方法。通过实例详细解析了如何处理大规模数据集上的区间友好对数计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接
题意:
给你一个序列,m次询问区间 [L,R] 内 i<j且 |ai-aj|<=k的友好对数的个数。

思路:
莫队+离散化+树状数组
将序列元素+k,-k,和原本的值保存在一个数组中,由于序列元素很大,需离散化处理;将询问区间存储,离线状态下用莫队进行查询;不断插入、删除元素,用树状数组(用树状数组保存比较简单)求满足条件的区间元素个数并不断更新树状数组,逐个求得区间答案。

code:

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <string>
#define debug(x) cout << #x << ":" << x << endl;
#define bug cout << "********" << endl;
#define ios ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define fi first
#define se second
using namespace std;
typedef long long ll;
const double eps = 1e-10;
const int inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double pi = acos(-1.0);
const int mod = 1e9 + 7;
const int maxn = 3e4 + 10;

int len,sum[maxn<<2],n,m,k;
int s[maxn],p[maxn<<2],block,ans[maxn];
int minn[maxn],maxx[maxn],r[maxn];//离散后的序列号

struct node
{
    int l,r,id;
    bool operator<(const node &a){
        return l/block==a.l/block ? r<a.r : l<a.l;
    }
}t[maxn];

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

void update(int x,int b)
{//更新
    while(x<=len){
        sum[x]+=b;
        x+=lowbit(x);
    }
}

int query(int x)
{//区间求和
    int ret=0;
    while(x){
        ret+=sum[x];
        x-=lowbit(x);
    }
    return ret;
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    int tot=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&s[i]);
        p[++tot]=s[i]-k,p[++tot]=s[i]+k,p[++tot]=s[i];//保存s[i]+k、s[i]-k、s[i]的值
    }
    block=sqrt(n);
    for(int i=1;i<=m;i++){
        scanf("%d%d",&t[i].l,&t[i].r);
        t[i].id=i;
    }
    sort(p+1,p+tot+1);
    len=unique(p+1,p+tot+1)-p;//数组去重离散化
    for(int i=1;i<=n;i++){//离散化后得到的相应序号
        r[i]=lower_bound(p+1,p+len+1,s[i])-p;
        minn[i]=lower_bound(p+1,p+len+1,s[i]-k)-p;
        maxx[i]=lower_bound(p+1,p+len+1,s[i]+k)-p;
    }
    sort(t+1,t+m+1);
    int L=1,R=0,ret=0;
    //莫队算法
    for(int i=1;i<=m;i++){
        while(t[i].l<L){
            L--;//如果要插入元素,先查询区间和,再更新树状数组
            ret+=query(maxx[L])-query(minn[L]-1);
            update(r[L],1);
        }
        while(t[i].l>L){
            update(r[L],-1);//如果要删除元素,先更新树状数组,再求区间和,下面的原理一样
            ret-=query(maxx[L])-query(minn[L]-1);
            L++;
        }
        while(t[i].r>R){
            R++;
            ret+=query(maxx[R])-query(minn[R]-1);
            update(r[R],1);
        }
        while(t[i].r<R){
            update(r[R],-1);
            ret-=query(maxx[R])-query(minn[R]-1);
            R--;
        }
        //离线状态下保存对应区间的友好对数个数
        ans[t[i].id]=ret;
    }
    for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值