欲望以提升热忱,毅力以磨平高山!|

XichenOC

园龄:1个月粉丝:4关注:0

2025-01-20 20:36阅读: 6评论: 0推荐: 0

莫队套分块-学习笔记

莫队套分块

P4396 [AHOI2013] 作业

题目翻译:

给定一个长度为 \(n\) 的序列,\(m\) 次询问,每一次给出 \(l,r,a,b\) 及求在区间 \([l,r]\) 间在值域 \([a,b]\) 的所有数的个数,和数的种数。

算法理解:

莫队套分块,显而易见就是在运用莫队的前提下,用分块来处理莫队的增减值。分块的复杂度为 \(m\sqrt n\) 而莫队的复杂度为 \(n \sqrt m\) 因此莫队套分块的复杂度为 \(O(n \sqrt m + m \sqrt n)\),分块的作用就是在增减值时若要枚举大量数据时可以用分块优化

思路:

\(1.\) 分析题目发现,我们可以用莫队来枚举区间,每一次枚举就将对应的值用桶储存下来,最后将要求值域的内的所有个数加上即可(求种数只用加一)。这样的复杂度为\(O(m \sqrt n + maxn\times n)\)\(maxn\) 指值域最大值)

\(2.\) 考虑优化,既然是用分块,那我们可以将值域分成 \(\sqrt {maxn}\),再用数组储存块的左右端点,这样累加答案时只需要加上块的答案,和多余的单独答案即可,复杂度为 \(O(m\sqrt n+m\sqrt {maxn})\) (对于种数也是同理)

完整代码:

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int block[N],block2[N],a[N],num[N];
int br[N],bl[N];
struct Q{
    int l,r,a,b,id;
}q[N];
bool cmp(Q x,Q y){
    if(block[x.l]!=block[y.l])return x.l<y.l;
    if(block[x.l]&1)return x.r<y.r;
    return x.r>y.r;
}
int sum[N],sum1[N];
bool vis[N];
void update(int x,int val){
    num[x]+=val;
    sum[block2[x]]+=val;
    if(num[x]==1 && val==1){
        vis[x]=1;
        sum1[block2[x]]++;
    }
    if(num[x]==0 && val==-1){
        vis[x]=0;
        sum1[block2[x]]--;
    }
}

int query(int a,int b){
    if(a>b)return 0;
    int ans=0;
    if(block2[a]==block2[b]){
        for(int i=a;i<=b;i++){
            if(vis[i])ans+=num[i];
        }
        return ans;
    }
    else{
        for(int i=a;i<=br[block2[a]];i++){
            ans+=num[i];
        }
        for(int i=bl[block2[b]];i<=b;i++){
            ans+=num[i];
        }
        for(int i=block2[a]+1;i<=block2[b]-1;i++){
            ans+=sum[i];
        }
        return ans;
    }
}
int query2(int a,int b){
    if(a>b)return 0;
    int ans=0;
    if(block2[a]==block2[b]){
        for(int i=a;i<=b;i++){
            if(vis[i])ans++;
        }
        return ans;
    }
    else{
        for(int i=a;i<=br[block2[a]];i++){
            ans+=vis[i];
        }
        for(int i=bl[block2[b]];i<=b;i++){
            ans+=vis[i];
        }
        for(int i=block2[a]+1;i<=block2[b]-1;i++){
            ans+=sum1[i];
        }
        return ans;
    }
}
void add(int x){
    update(a[x],1);
}
void del(int x){
    update(a[x],-1);
}
int ans1[N],ans2[N];
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    int len=1.0*n/sqrt(m)+1;
    for(int i=1;i<=n;i++){
        block[i]=(i-1)/len+1;
    }
    int maxn=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        maxn=max(maxn,a[i]);
    }
    len=sqrt(maxn);
    for(int i=1;i<=maxn;i++){
        block2[i]=(i-1)/len+1;
    }
    int blocknum=block2[maxn];
    for(int i=1;i<=blocknum;i++){
        bl[i]=(i-1)*len+1;
        br[i]=i*len;
    }
    blocknum=block2[maxn];
    for(int i=1;i<=m;i++){
        scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].a,&q[i].b);
        q[i].b=min(q[i].b,maxn);
        q[i].id=i;
    }
    sort(q+1,q+m+1,cmp);
    int l=1,r=0;
    for(int i=1;i<=m;i++){
        while(l<q[i].l)del(l++);
        while(l>q[i].l)add(--l);
        while(r<q[i].r)add(++r);
        while(r>q[i].r)del(r--);
        ans1[q[i].id]=query(q[i].a,q[i].b);
        ans2[q[i].id]=query2(q[i].a,q[i].b);
    }
    for(int i=1;i<=m;i++){
        printf("%d %d\n",ans1[i],ans2[i]);
    }
}

本文作者:XichenOC

本文链接:https://www.cnblogs.com/XichenOC/p/18682497

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   XichenOC  阅读(6)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起