分块算法

有一种很常见的题型  就是一个在一个好长的序列中进行 乍一看都是很麻烦的那种 修改和查询。

通常这种题 都是拿高端的数据结构 轻轻松松优化查询修改复杂度写过去,可是不会怎么办??!

于是就可以利用分块这种小技巧了。

这个技能就算是带奇技淫巧的暴力吧,暴力出奇迹。

简单来说 : 这种思路就是 把序列分成很多个块, 建立块与块之间的联系,然后每次修改就只需要改一个块里的东西。

如果分成 √N 块,每块有 √N 个 , 那么每次修改 查询 的操作 就都被减到了以前的根号级别, 所以就可以水过好多题

看下面两道例题,写完就会分块了。

[HNOI2010]BOUNCE 弹飞绵羊

原链接https://www.luogu.org/problem/show?pid=1393

题的意思是,地上沿一条直线摆了N个弹簧,第i个弹簧弹力值是Ki ,代表这个弹簧会把绵羊弹飞到位置 i+Ki,若不存在 i+Ki则绵羊被弹飞。M次操作,询问操作是给定一个起点x,求小绵羊从x出发被弹几次后被弹飞,修改操作是把第i个弹簧的弹力值修改为k

N<=200000,M<=100000

这个题的标程是LCT(link-cut-tree)  (有朝一日我学了会回来发题解的)

做法是先分块,(求一下每个块的l,r 每个点的belong)

    block=( int)sqrt(N);
    cnt=(N%block)?N/block+1:N/block;
    for(int i=1;i<=cnt;i++)
        l[i]=r[i-1]+1,r[i]=l[i]+block-1;
    r[cnt]=N;
    for(int i=1,j=1;i<=cnt;j++)
    {
        belong[j]=i;
        if(j==r[i])i++;
    }

然后 算一下 每个弹簧 可以弹到 下一个块的什么位置(还有步数)

 for(int i=N;i>=1;i--)
    {
        to[i]=i+a[i];
        if(to[i]>r[belong[i]])step[i]=1;
        else step[i]=step[to[i]]+1,to[i]=to[to[i]];
    }

就维护上面这些,所以每次修改只用改一个块里的东西

然后询问的时候就是一块一块的跳 累加上步数。

while(M--)
    {
        int k,x,y;
        read(k);
        if(k==1)    //query
        {
            ans=0;read(x);x++;
            while(x<=N)ans+=step[x],x=to[x];
            printf("%d\n",ans);
        }
        else        //change
        {
            read(x);read(y);x++;
            a[x]=y;
            for(int i=r[belong[x]];i>=l[belong[x]];i--)
            {
                to[i]=i+a[i];
                if(to[i]>r[belong[i]])step[i]=1;
                else step[i]=step[to[i]]+1,to[i]=to[to[i]];
            }
        }
    }

 1 #include<bits/stdc++.h>
 2 #define MAXN 200001
 3 void read(int& x)
 4 {
 5     x=0;char c;c=getchar();
 6     while(c<‘0‘||c>‘9‘)c=getchar();
 7     while(c>=‘0‘&&c<=‘9‘){x=x*10+c-‘0‘;c=getchar();}
 8 }
 9 int N,M,block,cnt,ans,a[MAXN],l[MAXN],r[MAXN],step[MAXN],to[MAXN],belong[MAXN];
10 int main()
11 {
12     read(N);
13     for(int i=1;i<=N;i++)read(a[i]);
14 //initiation
15     block=( int)sqrt(N);
16     cnt=(N%block)?N/block+1:N/block;
17     for(int i=1;i<=cnt;i++)
18         l[i]=r[i-1]+1,r[i]=l[i]+block-1;
19     r[cnt]=N;
20     for(int i=1,j=1;i<=cnt;j++)
21     {
22         belong[j]=i;
23         if(j==r[i])i++;
24     }
25     for(int i=N;i>=1;i--)
26     {
27         to[i]=i+a[i];
28         if(to[i]>r[belong[i]])step[i]=1;
29         else step[i]=step[to[i]]+1,to[i]=to[to[i]];
30     }
31 //operation
32     read(M);
33     while(M--)
34     {
35         int k,x,y;
36         read(k);
37         if(k==1)    //query
38         {
39             ans=0;read(x);x++;
40             while(x<=N)ans+=step[x],x=to[x];
41             printf("%d\n",ans);
42         }
43         else        //change
44         {
45             read(x);read(y);x++;
46             a[x]=y;
47             for(int i=r[belong[x]];i>=l[belong[x]];i--)
48             {
49                 to[i]=i+a[i];
50                 if(to[i]>r[belong[i]])step[i]=1;
51                 else step[i]=step[to[i]]+1,to[i]=to[to[i]];
52             }
53         }
54     }
55     return 0;
56 }

完整代码

[CQOI2011]动态逆序对

原链接 https://www.luogu.org/problem/show?pid=3157

题意是有一个长度为N的序列,M次操作,每次删一个数并求逆序对个数

N<=100000,M<=50000

正经做法里有 线段树+BST的 CDQ分治的 还有主席树?

分块做法 : 每个块都存在一个树状数组里。(不懂的话去写一个树状数组求逆序对的模板=、=)

先算一下修改之前的逆序对个数,

减去其它块的贡献(贡献直接拿树状数组查询)   自己块里的贡献就直接直接遍历一遍。

复杂度差不多是N*√N*logN(应该可以卡过去)

就贴个核心操作部分的代码吧,很好理解。

//    operation
    while(M--)
    {
        int x;cin>>x;
    //    calc
        for(int i=1;i<belong[x];i++)
            ans-=siz-query(c[belong[x]],a[x]);
        for(int i=l[belong[x]];i<x;i++)
            if(a[i]>a[x])ans--;
        for(int i=x+1;i<=r[belong[x]];i++)
            if(a[x]>a[i])ans--;
        for(int i=belong[x]+1;i<=block;i++)
            ans-=query(c[belong[x]],a[x]-1);
        cout<<ans<<" ";
    //    update
        add(c[belong[x]],a[x],-1);
    }

千言万语汇成一句话:

天下武功出暴力

时间: 2024-08-14 11:02:19

分块算法的相关文章

分块算法及模板

此文为博主原创,转载时请通知博主,并把原文链接放在正文醒目位置. 简要介绍 分块算法就是把一串数据分割成几块数据的算法,其实是对暴力的一种优化. 通常在分块时,每块的大小为√n.但最后一块的大小也可能小于√n,只能用暴力来算. 通过把对单个数据的操作转化为对几个块的数据的操作,能够节省时间,提高运算效率. 分块算法在处理大范围的修改.查询问题时有很大优势. 分块算法代码 1 /*此代码主要模仿了钟皓曦大佬的分块算法*/ 2 #include<iostream> 3 #include<cs

hdu 5057 Argestes and Sequence(分块算法)

Argestes and Sequence Time Limit: 5000/2500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 566    Accepted Submission(s): 142 Problem Description Argestes has a lot of hobbies and likes solving query problems espec

【快速处理】分块算法

分块算法 ---------------------------------------------------------- 1.思想 如果我们需要对一个特定的序列进行操作,那么非常直观.简单的方法就是纯暴力(不,那叫模拟). 不过如果暴力能过的话,那就呵呵了. 所以我们要想一些比较高能的数据结构——分块. 相比线段树来说,分块算法比较难实现,但是只要深入理解,就可以实现了,只不过需要一些数据结构的辅助. 分块实质来说就是把一个序列切分,从而实现对查询.查找.替换等等操作的高效处理. ----

刷题总结——分块算法(bzoj2821作诗)

题目: Description 神犇SJY虐完HEOI之后给傻×LYD出了一题:SHY是T国的公主,平时的一大爱好是作诗.由于时间紧迫,SHY作完诗 之后还要虐OI,于是SHY找来一篇长度为N的文章,阅读M次,每次只阅读其中连续的一段[l,r],从这一段中选出一 些汉字构成诗.因为SHY喜欢对偶,所以SHY规定最后选出的每个汉字都必须在[l,r]里出现了正偶数次.而且SHY认 为选出的汉字的种类数(两个一样的汉字称为同一种)越多越好(为了拿到更多的素材!).于是SHY请LYD安排选 法.LYD这

c++分块算法(暴力数据结构)

快要noip了,该写些题解攒攒rp了(逃) 看到题解里那么多线段树啊,树状数组啊,本蒟蒻表示:这都是什么鬼东西? 在所有高级数据结构中,树状数组是码量最小的,跑的也基本是最快的,但理解很难,并且支持的操作很少:线段树的码量,相信写过线段树题的童鞋都亲身体验过这种恐怖(那些3min写完splay的巨佬不要d我),理解虽然简单,但一题调一辈子啊! 所以说到这里,本蒟蒻想表达什么呢? 分块大法吼啊! 有人会说:分块不是n√n的复杂度吗?怎么能跟nlogn的数据结构相提并论呢?或者说,分块在联赛中,有什

算法复习——分块算法

题目: Description Input 修正一下 l = (l_0 + x - 1) mod n + 1, r = (r_0 + x - 1) mod n + 1 Output Sample Input 6 3 1 2 3 2 1 2 1 5 3 6 1 5 Sample Output 1 2 1 HINT 修正下: n <= 40000, m <= 50000 Source Vani原创 题解: 题解如上·····一个分块模板题打了我2个小时·····不得不说代码能力和专注度还不够···

分块算法总结

最近学了一些分块,来总结一下个人感受: 分块是什么呢?就是把一个大块拆成若干个小块进行计算,每个小块有可能有一些共同特点,或者每个小块内部是有顺序的,这样,在修改操作的时候,只需要算出两边的节点所属的小块的编号分别是什么,然后两边的散块(姑且这么叫)可以进行暴力修改,而中间的由于题目不同,可以进行不同的操作,比如说区间加,那么就可以在中间的每个小块上面直接像线段树一样打一个lazy标记,标记区间增加了多少.然后在查询的时候就可以直接对散块进行暴力查询,然后对整块运用之前设置的lazy标记,或者保

分块算法板子

https://www.luogu.org/problemnew/show/1903 用pre[i]数组记录上一次和当前i同色的位置 查询[l,r],若pre[i]<r,则说明在[l,i)区间内没用和i同色的,则++ans 于是就可以大胆地分块 对于每一块按照pre[i]排序,再进行二分了 复杂度O(qsqrt(n)log(n)). #include<stdio.h> #include<math.h> #include<algorithm> #define FOR

分块,莫队算法总结

分块算法总结 分块,就是一种暴力算法,不过复杂度优于暴力,是基于足够的预处理和合理可行的维护操作进行优化时间, 在预处理+维护的耗时上与暴力处理的耗时上找到一种平衡,于是出了这个优美的算法 标志:查询某一区间内元素种类数,查询某一区间大于等于某一元素的数的个数(即排名 模板:LuoguP2801 教主的魔法 为了查询大于等于C的个数,可以排序,用区间长度-C的排名就是 答案数. 所以可以动态维护一个块内有序的数组 #include<bits/stdc++.h> #define re regis