zoukankan      html  css  js  c++  java
  • 分块(简单版树状数组,线段树)

    前言

    首先,在NOIP的比赛里分块是一个很好的水分神器,因为它可以代替树状数组,线段树,但是如果出题人要卡你的程序的话......

    分块思想

    包含n个元素的整数数组A,每次可以 C(i, j) : 修改一个元素A[i] = j Q(i, j) : 询问A[i]+A[i+1]+…+A[j]的值

    如何设计算法,使得修改和询问操作的时间复杂度尽量低? 显然存在修改O(1),查询O(n)的算法。

    每次重新计算,一些未被修改区间也经常被重复求和,于是我们可以将A[1..n]均分成sqrt(n)块,每块内部的元素为sqrt(n)个。

    修改操作时只需要修改A[i]及A[i]所在的块。

    询问操作时是需要枚举i和j所在的块内部,及其之间的块。

    时间复杂度在O(sqrt(n))级别。

    主要思想就是将待操作的长度为N的区间分成大小为sqrt(N)的块,然后实现各种操作……

    一些常用定义:

      MAGIC:定义一个块的大小,如字面意思,一个莫名其妙的数字……

      于是,我们把一段长度为N的区间,分成了若干长度为 MAGIC 的区间:[0,magic),[magic, 2magic)....

       于是易得,i / MAGIC 就是点 i 所在块的编号,若 i % MAGIC == 0,则证明由点 i 开始是一个新区间

    一般来讲,我们在预处理和修改的时候,维护两个信息,一个是序列,另一个是块

    例题

    讲了这么多还是来看例题吧......

    链接点我!!!

    #6277. 数列分块入门 1

    题目描述

    给出一个长为 n的数列,以及 个操作,操作涉及区间加法,单点查值。

    输入格式

    第一行输入一个数字 n

    第二行输入 n 个数字,第 i 个数字为 ai​​,以空格隔开。

    接下来输入 n 行询问,每行输入四个数字 optlr、c,以空格隔开。

    若 opt=0,表示将位于 [l,r] 的之间的数字都加 c

    若 opt=1,表示询问 a的值(l 和 c 忽略)。

    输出格式

    对于每次询问,输出一行一个数字表示答案。

    样例

    样例输入

    4
    1 2 2 3
    0 1 3 1
    1 0 1 0
    0 1 2 2
    1 0 2 0

    样例输出

    2
    5

    数据范围与提示

    对于 100% 的数据,1≤n≤50000,−231≤othersans≤231−1

    题解:

      (建议大家边看程序边题解)

      这是一题区间修改,单点查询可以用树状数组,线段树(这不是放*吗

      但是我们在这里考虑分块;

      首先,我们开一个最普通的数组来记录每个元素的权值;

      然后再开一个数组来记录每个元素所对应的分块;

      然后再再(语文有点不好......)开一个数组来记录每个分块要加的值;

      每次寻找 l , r ,之间的区间,在分块内部的直接打上标记;

      如果在分块外面的直接暴力修改;

      嗯,代码......

     1 //NOIPRP++
     2 #include<bits/stdc++.h>
     3 #define Re register int
     4 using namespace std;
     5 int N,a[50005],f[50005],opt,l,r,c,Sqr,Tage[50005]; 
     6 inline void read(int &x){
     7     x=0; char c=getchar(); bool p=1;
     8     for (;'0'>c||c>'9';c=getchar()) if (c=='-') p=0;
     9     for (;'0'<=c&&c<='9';c=getchar()) x=(x<<1)+(x<<3)+(c^48);
    10     p?:x=-x;
    11 }
    12 inline void Add(int l,int r,int c){
    13     for (Re i=l;i<=min(f[l]*Sqr,r);i++) a[i]+=c;
    14     if (f[l]^f[r]) for (Re i=(f[r]-1)*Sqr+1;i<=r;i++) a[i]+=c;
    15     for (Re i=f[l]+1;i<=f[r]-1;i++) Tage[i]+=c;
    16 }
    17 int main(){
    18     Re i,j;
    19     read(N);Sqr=sqrt(N);
    20     for (i=1;i<=N;i++) f[i]=(i-1)/Sqr+1;
    21     for (i=1;i<=N;i++) read(a[i]);
    22     for (i=1;i<=N;i++){
    23         read(opt);read(l);read(r);read(c);
    24         if (opt==0) Add(l,r,c);
    25         if (opt==1) printf("%d
    ",a[r]+Tage[f[r]]);
    26     }
    27     return 0;
    28 }
    29 //NOIPRP++
    View Code

     这里由于博主懒,所以就不再讲其他扩展的例题了,如果有大佬还想要更多有关分块的好题的话请点我,当然也不能少了这些题目的题解......

  • 相关阅读:
    发送邮件
    C#操作Excel总结
    注意!监控MySQL服务是否正常,懂这4种方法就可以了
    Linux磁盘空间爆满怎么办?定时文件清理脚本配置实现
    Linux 服务器必备的安全设置,建议收藏!
    MySQL入门到精通:MySQL 选择数据库
    TIOBE3月榜单公布!C 语言稳居第一,将新增功能,消除差异
    C++如何读取带空格字符串?这5种方法教会你
    C语言丨二分查找算法详解(含示例代码)
    线上故障了!居然是因为Linux磁盘缓存机制导致的
  • 原文地址:https://www.cnblogs.com/to-the-end/p/9919021.html
Copyright © 2011-2022 走看看