bzoj3236 [Ahoi2013]作业(莫队+分块)

本文介绍了一种使用莫队算法处理区间查询的方法,并结合分块技巧优化复杂度至O((n+m)n−√)。通过具体代码实现展示了如何利用该算法进行区间求和及统计不同元素数量,适用于解决大规模数据集上的区间查询问题。

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

对查询莫队处理,查询区间和分块处理。
复杂度O((n+m)n)
如果用莫队+BIT的话,复杂度是O(nnlogn)
其实也是树套树裸题?O(mlog2n)
都应该过不去才对(划掉)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(T==S){T=(S=buf)+fread(buf,1,1<<16,stdin);if(S==T) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=gc();}
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=gc();
    return x*f;
}
int n,m,a[N],cnt[N],nn,bel[N],sum1[400],sum2[400],ans1[N*10],ans2[N*10];
bool inq[N];
struct quer{
    int l,r,a,b,id;
    friend bool operator<(quer a,quer b){return bel[a.l]==bel[b.l]?a.r<b.r:bel[a.l]<bel[b.l];}
}q[N*10];
inline void change(int p){
    if(inq[p]){
        sum1[bel[a[p]]]--;if(--cnt[a[p]]==0) sum2[bel[a[p]]]--;
    }else{
        sum1[bel[a[p]]]++;if(++cnt[a[p]]==1) sum2[bel[a[p]]]++;
    }inq[p]^=1;
}
inline void ask(int x,int y,int id){
    if(bel[x]==bel[y]){
        for(int i=x;i<=y;++i) ans1[id]+=cnt[i],ans2[id]+=(cnt[i]?1:0);return;
    }int r=min(bel[x]*nn,n);
    for(int i=x;i<=r;++i) ans1[id]+=cnt[i],ans2[id]+=(cnt[i]?1:0);
    for(int i=(bel[y]-1)*nn+1;i<=y;++i) ans1[id]+=cnt[i],ans2[id]+=(cnt[i]?1:0);
    for(int i=bel[x]+1;i<bel[y];++i) ans1[id]+=sum1[i],ans2[id]+=sum2[i];
}
int main(){
//  freopen("a.in","r",stdin);
    n=read();m=read();nn=sqrt(n);
    for(int i=1;i<=n;++i) a[i]=read(),bel[i]=(i-1)/nn+1;
    for(int i=1;i<=m;++i) q[i].l=read(),q[i].r=read(),q[i].a=read(),q[i].b=read(),q[i].id=i;
    sort(q+1,q+m+1);int l=1,r=0;
    for(int i=1;i<=m;++i){
        while(r>q[i].r) change(r--);
        while(r<q[i].r) change(++r);
        while(l<q[i].l) change(l++);
        while(l>q[i].l) change(--l);
        ask(q[i].a,q[i].b,q[i].id);
    }for(int i=1;i<=m;++i) printf("%d %d\n",ans1[i],ans2[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值