分块,莫队算法总结

分块算法总结

  分块,就是一种暴力算法,不过复杂度优于暴力,是基于足够的预处理和合理可行的维护操作进行优化时间,

 在预处理+维护的耗时上与暴力处理的耗时上找到一种平衡,于是出了这个优美的算法

  标志:查询某一区间内元素种类数,查询某一区间大于等于某一元素的数的个数(即排名

 模板:LuoguP2801 教主的魔法

  为了查询大于等于C的个数,可以排序,用区间长度-C的排名就是 答案数。

  所以可以动态维护一个块内有序的数组

#include<bits/stdc++.h>
#define re register
#define inc(i,j,k) for(re int i=j;i<=k;++i)
#define il inline
using namespace std;
const int maxn=1000010;
vector <int> ve[1010];
int v[maxn],bl[maxn],atag[maxn];
int n,m,base;
il void reset(int x) //重新维护一个块内元素的顺序
{
    ve[x].clear();
    inc(i,(x-1)*base+1,min(x*base,n)) ve[x].push_back(v[i]);//别忘了容易溢出n的数组大小呀
    sort(ve[x].begin(),ve[x].end());
}
il void add(int a,int b,int c)//区间加
{
    inc(i,a,min(bl[a]*base,b)) v[i]+=c;//暴力处理前半部分
    reset(bl[a]);
    if(bl[a]!=bl[b]) {inc(i,(bl[b]-1)*base+1,b) v[i]+=c; reset(bl[b]);}//暴力处理后面部分
    inc(i,bl[a]+1,bl[b]-1) atag[i]+=c;
}
il int query(int a,int b,int c)//区间查询
{
    int ans=0;
    inc(i,a,min(bl[a]*base,b)) if(v[i]+atag[bl[a]]<c) ans++; //暴力处理前部分
    if(bl[a]!=bl[b])
    {
        inc(i,(bl[b]-1)*base+1,b) if(v[i]+atag[bl[b]]<c) ans++;//暴力处理后面部分
    }
    inc(i,bl[a]+1,bl[b]-1)//对于块来说,直接减去lazy_tag的影响
    {
        int x=c-atag[i];
        ans+=lower_bound(ve[i].begin(),ve[i].end(),x)-ve[i].begin();//查询第一个大于等于x的数的位置
    }
    return ans;
}
int main()
{
    scanf("%d %d",&n,&m);
    base=sqrt(n);
    inc(i,1,n) scanf("%d",&v[i]),bl[i]=(i-1)/base+1,ve[bl[i]].push_back(v[i]);
    inc(i,1,bl[n]) sort(ve[i].begin(),ve[i].end());
    char s;
    re int x,y,z;
    inc(i,1,m)
    {
        s=getchar();
        while(s!=‘A‘&&s!=‘M‘) s=getchar();
        if(s==‘M‘)
        {
            scanf("%d %d %d",&x,&y,&z);
            add(x,y,z);
        }
        else
        {
            scanf("%d %d %d",&x,&y,&z);
            printf("%d\n",y-x+1-query(x,y,z));
        }
    }
}

其他例题先咕着吧

然后...

莫队

莫队其实也是基于分块的算法,主要是一种思想吧我认为,基于对询问问题的离线处理按一定顺序去处理问题

然后保证了暴力的复杂度

具体实现是要按左区间的块排序,快相同的按右端点排序,保证同一个块内复杂度O(n),共有sqrt(n)块

所以总复杂度 O(n*sqrt(n))

主要用于查询元素的种类数

模板:LuoguP2709 小B的询问

#include<bits/stdc++.h>
#define ll long long
#define re register
using namespace std;
#define maxn 50005
struct query
{
    int l,r,id,pos;
//    friend bool operator < (query xx,query yy)
//    {    if(xx.pos==yy.pos) return xx.l<yy.r;    else return xx.pos<yy.pos;} //这里是按奇偶性排序,可以先忽略
    bool operator <(const query &x) const {if(pos==x.pos)return r<x.r;
   else return pos<x.pos;}
}a[maxn];
int b[maxn],n,m,K;ll cnt[maxn],Ans[maxn];
inline int read(){
    int f=1,x=0;char ch;
    do{ch=getchar();if(ch==‘-‘)f=-1;}while(ch<‘0‘||ch>‘9‘);
    do{x=x*10+ch-‘0‘;ch=getchar();}while(ch>=‘0‘&&ch<=‘9‘);
    return x*f;
}
int main()
{
//    freopen("testdata.in.txt","r",stdin);
//    freopen("4321.txt","w",stdout);
    n=read(); m=read(); K=read();
    int siz=(int) sqrt(n);
    for(re int i=1;i<=n;++i) b[i]=read();
    for(re int i=1;i<=m;++i)
    {
        a[i].l=read(); a[i].r=read(); a[i].id=i;
        a[i].pos=(a[i].l-1)/siz+1;
    }
    sort(a+1,a+m+1);
    int l=1,r=0; long long ans=0;
    for(re int i=1;i<=m;++i)
    {
        while(l>a[i].l) { l--; cnt[b[l]]++; ans+= 2*cnt[b[l]]-1 ;} //外拓的时候,先加减再统计
        while(r<a[i].r) { r++; cnt[b[r]]++; ans+= 2*cnt[b[r]]-1 ;}
        while(r>a[i].r) {cnt[b[r]]--; ans-= 2*cnt[b[r]]+1 ; r--; } //回溯的时候,先统计再加减
        while(l<a[i].l) {cnt[b[l]]--; ans-= 2*cnt[b[l]]+1 ; l++; }
        Ans[a[i].id]=ans;
    }
    for(re int i=1;i<=m;++i) printf("%lld\n",Ans[i]);
}

其次就是对于莫队的修改操作了,带修莫队与原版的差异是多了个时间轴

把修改操作看作删除旧的+增加新的,这种思想很多时候都用的上

也是裸暴力,只不过多关注时间轴上修改都干了什么,这样查询的时候只考虑当前时间之前的操作就好了

分块大小为 (n*t)1/3  复杂度O((n4*t)1/3)   ->不会证

模板题LuoguP1903 数颜色

//窝洛谷突然看不了自己提交的代码了。。。先复制第一篇题解的把,,等哪天我想起来再更(逃

#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define fo(a,b,c) for(int a=b;a<=c;a++)
#define go(a,b,c) for(int a=b;a>=c;a--)
int read(){
    int a=0,f=0;char c=getchar();
    for(;c<‘0‘||c>‘9‘;c=getchar())if(c==‘-‘)f=1;
    for(;c>=‘0‘&&c<=‘9‘;c=getchar())a=a*10+c-‘0‘;
    return f?-a:a;
}
const int N=10001;
int a[N],p[1000001],ans[N],divi;
struct nod{int pla,pre,suc;}cg[N];
struct node{int l,r,t,bel;}ls[N];
int cmp(node a,node b){
    if(a.l/divi!=b.l/divi)return a.l/divi<b.l/divi;
    if(a.r/divi!=b.r/divi)return a.r/divi<b.r/divi;
    return a.t<b.t;
}
int main(){
    int n=read(),m=read(),ln=0,tn=0,l=1,r=0,t=0,num=0;
    fo(i,1,n)a[i]=read();
    fo(i,1,m){
        scanf("\n");
        if(getchar()==‘R‘){//如果读入修改则记录修改的地点,修改前的数字和修改后的数字
            ++tn;cg[tn].pla=read(),cg[tn].suc=read();
            cg[tn].pre=a[cg[tn].pla];
            a[cg[tn].pla]=cg[tn].suc;
        }
        else ls[++ln]=(node){read(),read(),tn,ln};
    }
    divi=ceil(exp((log(n)+log(tn))/3));//分块大小
    go(i,tn,1)a[cg[i].pla]=cg[i].pre;
    sort(ls+1,ls+ln+1,cmp);
    fo(i,1,m){
        while(ls[i].l<l)num+=!p[a[--l]]++;
        while(ls[i].l>l)num-=!--p[a[l++]];//l移动
        while(ls[i].r>r)num+=!p[a[++r]]++;
        while(ls[i].r<r)num-=!--p[a[r--]];//r移动
        while(ls[i].t<t){
            int pla=cg[t].pla;
            if(l<=pla&&pla<=r)num-=!--p[a[pla]];
            a[pla]=cg[t--].pre;
            if(l<=pla&&pla<=r)num+=!p[a[pla]]++;
        };
        while(ls[i].t>t){
            int pla=cg[++t].pla;
            if(l<=pla&&pla<=r)num-=!--p[a[pla]];
            a[pla]=cg[t].suc;
            if(l<=pla&&pla<=r)num+=!p[a[pla]]++;
        };//t移动
        ans[ls[i].bel]=num;
    }
    fo(i,1,ln)printf("%d\n",ans[i]);
    return 0;
}

二次离线莫队

updataing......

原文地址:https://www.cnblogs.com/ZzTzZ/p/11643406.html

时间: 2024-10-16 17:14:34

分块,莫队算法总结的相关文章

【BZOJ-3809】Gty的二逼妹子序列 分块 + 莫队算法

3809: Gty的二逼妹子序列 Time Limit: 80 Sec  Memory Limit: 28 MBSubmit: 1072  Solved: 292[Submit][Status][Discuss] Description Autumn和Bakser又在研究Gty的妹子序列了!但他们遇到了一个难题. 对于一段妹子们,他们想让你帮忙求出这之内美丽度∈[a,b]的妹子的美丽度的种类数. 为了方便,我们规定妹子们的美丽度全都在[1,n]中. 给定一个长度为n(1<=n<=100000)

莫队算法---基础知识介绍(转载)

莫队算法 莫队算法可用于解决一类可离线且在得到区间[l,r][l,r]的答案后,能在O(1)O(1)或O(log2n)O(log2?n)得到区间[l,r+1][l,r+1]或[l−1,r][l−1,r]的答案的问题 先看这样一个问题: 给出n个数字,m次询问,每次询问在区间[li,ri][li,ri]之间任选两个数字相等的概率是多少.(n,q<=50000)(小z的袜子) 在区间[l,r][l,r]中,这个概率是: ∑vi=1C(2,f(i))C(2,r−l+1)∑i=1vC(2,f(i))C(

XOR and Favorite Number(莫队算法+分块)

E. XOR and Favorite Number time limit per test 4 seconds memory limit per test 256 megabytes input standard input output standard output Bob has a favorite number k and ai of length n. Now he asks you to answer m queries. Each query is given by a pai

【莫队算法】【权值分块】bzoj3920 Yuuna的礼物

[算法一] 暴力. 可以通过第0.1号测试点. 预计得分:20分. [算法二] 经典问题:区间众数,数据范围也不是很大,因此我们可以: ①分块,离散化,预处理出: <1>前i块中x出现的次数(差分): <2>第i块到第j块中的众数是谁,出现了多少次. 询问的时候,对于整块的部分直接获得答案:对于零散的部分,暴力统计每个数出现 的次数,加上差分的结果,尝试更新ans. 时间复杂度O(m*sqrt(n)), 空间复杂度O(n*sqrt(n)). ②考虑离线,莫队算法,转移的时候使用数据

【莫队算法】【权值分块】poj2104 K-th Number / poj2761 Feed the dogs

先用莫队算法保证在询问之间转移的复杂度,每次转移都需要进行O(sqrt(m))次插入和删除,权值分块的插入/删除是O(1)的. 然后询问的时候用权值分块查询区间k小值,每次是O(sqrt(n))的. 所以总共的复杂度是O(m*(sqrt(n)+sqrt(m)))的. 常数极小. 别的按权值维护的数据结构无法做到O(1)地插入删除. poj2104 的输出优化 别忘了处理负数. 完爆主席树,这份代码目前在 poj2761 上 Rank1. Rank Run ID User Memory Time

【BZOJ】3052: [wc2013]糖果公园 树分块+待修改莫队算法

[题目]#58. [WC2013]糖果公园 [题意]给定n个点的树,m种糖果,每个点有糖果ci.给定n个数wi和m个数vi,第i颗糖果第j次品尝的价值是v(i)*w(j).q次询问一条链上每个点价值的和或修改一个点的糖果ci.n,m,q<=10^5. [算法]树分块+带修改莫队算法 [题解]参考:WC 2013 糖果公园 park 题解 by vfleaking 首先树分块,参考王室联邦的方法.确定块大小为B,一遍DFS可以分成若干大小为[B,3B]的块,性质是块内两点距离至多为B. 定义(x,

莫队算法(离线区间查询问题)(莫队,分块)

莫队算法总结 前言: 又是和主席树一样,OI神犇们发明的算法. 刚听到算法的名字的时候就猜到莫队是一个大神犇了.没想到后来知道他竟然是我们的学长!未见其人,先闻其名啊! 直到后来CJ被定为PKUWC举办地,为了提供一个更好的环境,CJ翻新了一下机房,并且挂上了荣誉墙.于是我就能一睹NOI虐场的学长的真容了. 小学弟在学长的光环下其实是挺自豪的呢! 性质和原理 更新中...... 原文地址:https://www.cnblogs.com/flashhu/p/8410600.html

莫队算法

Beautiful Girl 题意 给定一个长度为 n 的序列 a[1], a[2], ..., a[n] . m 组询问 (l, r, K) , 求区间 [l, r] 去除重复的数之后的第 K 小. n, m <= 100000 . 分析 莫队算法 + 值域分块. 1 #include <cstdio> 2 #include <cstring> 3 #include <cstdlib> 4 #include <cctype> 5 #include &

BZOJ4241 历史研究 莫队算法 堆

欢迎访问~原文出处--博客园-zhouzhendong&AK 去博客园看该题解 题目 Description IOI国历史研究的第一人--JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. 日记中记录了连续N天发生的时间,大约每天发生一件. 事件有种类之分.第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大. JOI教授决定用如下的方法分析这些日记: 1.

(普通的)莫队算法简单介绍

莫队算法(由莫涛发明的)是一种离线的暴力算法(至少我这么认为).使用莫队算法的条件是,知道一个区间[l, r]的结果,那么也可以快速知道[l + 1, r],[l - 1, r], [l, r - 1], [l, r + 1]这四个区间的结果.于是可以想到,直接通过这样转移来解决一些问题.当然有些出题人机智,故意卡这种暴力,让你从头跑到尾然后从尾跑到头,于是时间复杂度高达O(n2) 而莫队算法就是通过改变处理询问的顺序来降低时间复杂度. 比如说现在知道一个区间[l1, r1],又要转移到[l2,