#莫队,分块#洛谷 5268 bzoj 5016 一个简单的询问

本文深入探讨了莫队算法的原理与实现细节,通过具体示例解释了如何利用该算法解决区间查询问题,特别是在时间复杂度为O(n^(3/2))的情况下,展示了其在处理大量数据时的优势。代码部分提供了完整的实现方案,包括关键的数据结构定义和更新操作。

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

题目


分析

要把这个询问拆成 g ( r 1 ) ∗ g ( r 2 ) − g ( l 1 − 1 ) ∗ g ( r 2 ) − g ( r 1 ) ∗ g ( l 2 − 1 ) + g ( l 1 − 1 ) ∗ g ( l 2 − 1 ) g(r1)*g(r2)-g(l1-1)*g(r2)-g(r1)*g(l2-1)+g(l1-1)*g(l2-1) g(r1)g(r2)g(l11)g(r2)g(r1)g(l21)+g(l11)g(l21)
那么对于 g ( l ) ∗ g ( r ) g(l)*g(r) g(l)g(r),如果要使其变成 g ( l + 1 ) ∗ g ( r ) g(l+1)*g(r) g(l+1)g(r),那么其实增加的是 g ( r ) g(r) g(r),相反同理,这样可以用莫队来做,时间复杂度 O ( n 3 2 ) O(n^{\frac{3}{2}}) O(n23)


代码

#include <cstdio>
#include <cctype>
#include <cmath>
#include <algorithm>
#define rr register
using namespace std;
const int N=50011;
struct rec{int l,r,bel,type;}q[N<<2];
int pos[N],a[N],cntl[N],cntr[N],n,m,now,block,ans[N];
inline signed iut(){
    rr int ans=0; rr char c=getchar();
    while (!isdigit(c)) c=getchar();
    while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    return ans;
}
inline void print(int ans){
    if (ans>9) print(ans/10);
    putchar(ans%10+48);
}
inline bool cmp(rec x,rec y){
    return pos[x.l]!=pos[y.l]?pos[x.l]<pos[y.l]:((pos[x.l]&1)?x.r<y.r:x.r>y.r);
}
inline void UpdL(int x,int op){cntl[a[x]]+=op,now+=op*cntr[a[x]];}
inline void UpdR(int x,int op){cntr[a[x]]+=op,now+=op*cntl[a[x]];}
signed main(){
    n=iut(); block=sqrt(n);
    for (rr int i=1;i<=n;++i) a[i]=iut(),pos[i]=i/block+1;
    m=iut(); rr int temp=0;
    for (rr int i=1;i<=m;++i){
        rr int xl=iut(),yl=iut(),xr=iut(),yr=iut();
        q[++temp]=(rec){yl,yr,i,1},
        q[++temp]=(rec){yl,xr-1,i,-1},
        q[++temp]=(rec){xl-1,yr,i,-1},
        q[++temp]=(rec){xl-1,xr-1,i,1};
    }
    sort(q+1,q+1+temp,cmp);
    for (rr int i=1,L=0,R=0;i<=temp;++i){
        while (L<q[i].l) UpdL(++L,1);
        while (L>q[i].l) UpdL(L--,-1);
        while (R>q[i].r) UpdR(R--,-1);
        while (R<q[i].r) UpdR(++R,1);
        ans[q[i].bel]+=now*q[i].type;
    }
    for (rr int i=1;i<=m;++i) print(ans[i]),putchar(10);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值