优秀数据结构1-分块(v1.0)

---恢复内容开始---

v1.0,征求大佬的修改意见

众所周知,欣隆妹妹是个毒瘤。

经常出毒瘤数据结构题。

出的最多的就是分块。

那么,窝们今天就来学习优(暴)秀(力)数据结构--分块

可爱的由乃。

QAQ

part-one 什么是分块?

分块,顾名思义,就是把序列分成相等的若干个块,对于每一个块,维护信息,然后再合并。

part-two 分块的原理,实现?

现在,我们有一个问题,对一个序列,区间加法,求区间每一个数的和(假设我们不会一种东西叫线段树)

暴力做法:

暴力修改,暴力查询,显然T飞,时间复杂度O(n²)。

考虑优化暴力:

把序列分成ω个块,每一个块都维护一个TAG。

l,r区间分为整块部分和零散块部分。

对于整块的部分,我们直接对块的TAG加上x。

对于零散的部分,我们暴力加。

查询也是一样。

对于整块的部分,就是块内原来的和+(n/ω)*TAG>

对于零散的部分,直接暴力查询。

时间复杂度。

整块最多ω个,所以时间复杂度O(ω)。

零散部分最多(n/ω)个,时间复杂度O(n/ω)。

显然ω=sqrt(n)是最优。

所以分块时间复杂度m*sqrt(n).

分块大概实现:

记录bel数组,代表一个点属于哪一个块。

记录lft,rgt数组,代表每一个块的左端点和右端点

然后对与每一个块统计信息。

对于每一次查询

若两个端点在同一个块中,则暴力查询。

否则先查询整块信息,再暴力查询零散部分

对于每一个修改。

整块信息直接打TAG,零散部分暴力修改。

part three-例题(因不同题写题时间可能差异较大,码风可能不同)

1.弹飞绵羊(luogu3203)

题目大意:Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。

基础题,对装置分块,记录每一个点跳几次能跳出当前所在块,跳出后跳到几号节点。

代码:

#include <iostream>#include <stdio.h>
#include <string>
#include <math.h>
using namespace std;
const int maxn=200005,maxm=505;
int n,m,Q,num[maxn],bel[maxn],sum[maxn],outt[maxn];
struct block {
    int l,r;
} a[maxn];
inline int read() {
    int x=0;
    char ch=getchar();
    while (ch<‘0‘||ch>‘9‘) ch=getchar();
    while (ch>=‘0‘&&ch<=‘9‘) x=x*10+ch-48,ch=getchar();
    return x;
}
void doit(int l,int r) {
    for (int i=r; i>=l; i--) if (i+num[i]>a[bel[i]].r) sum[i]=1,outt[i]=i+num[i];
        else sum[i]=sum[i+num[i]]+1,outt[i]=outt[i+num[i]];
}
int main() {
    n=read();
    m=sqrt(n);
    if (m*m<n) m++;
    int s=0;
    for (int i=1; i<=n; i++) num[i]=read();
    for (int i=1; i<=n; i+=m) a[++s].l=i,a[s].r=i+m-1;
    if (s<m) a[++s].l=s*m+1,a[s].r=n;
    s=1;
    for (int i=1; i<=n; i++) {
        if (i>a[s].r) s++;
        bel[i]=s;
    }
    doit(1,n);
    Q=read();
    while (Q--) {
        int x=read(),y=read()+1;
        if (x==1) {
            int ans=sum[y],x=outt[y];
            for (int i=bel[y]; i<=m&&x<=n; i++) ans+=sum[x],x=outt[x];
            printf("%d\n",ans);
        } else {
            int z=read();
            num[y]=z;
            doit(a[bel[y]].l,a[bel[y]].r);
        }
    }
    return 0;
}

2.lucky array(CF121E)

其实这题更简单,本该放在前面。

题目大意:区间加法,区间幸运数个数。

因为值域很小,我们发现在值域中的幸运数个数为30,所以我们先打个表,然后分块。

先统计每一个块内每一个数的出现次数,然后对于加法,我们还是打TAG,只不过在查询的时候要查询=(幸运数字-TAG)的个数

然后就好了。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Rint register int
#define Temp template<typename T>
#define update(x,y,z) num[x][y]--,num[x][y+=z]++
using namespace std;
Temp inline void read(T &x) {
    x=0;T w=1,ch=getchar();
    while(!isdigit(ch)&&ch!=‘-‘) ch=getchar();
    if(ch==‘-‘) w=-1,ch=getchar();
    while(isdigit(ch)) x=x*10+ch-‘0‘,ch=getchar();
    x*=w;
}
const int t[30]={4,7,44,47,74,77,444,447,474,477,744,747,774,777,4444,4447,4474,4477
,4744,4747,4774,4777,7444,7447,7474,7477,7744,7747,7774,7777};
const int m=317*5;
const int maxn=1e5+10;
int n,q,cnt;
int a[maxn];
int num[105][maxn],id[maxn],low[maxn],high[maxn],add[maxn];
int vis[10010];
int main() {
    read(n);read(q);
    for (int i=1;i<=n;++i) {
        read(a[i]);
        id[i]=i/m+1;
        num[id[i]][a[i]]++;
    }
    cnt=id[n];
    for (int i=1;i<=cnt;++i) {
        low[i]=(i-1)*m;
        high[i]=i*m-1;
    }
    low[1]=1;high[cnt]=n;
    for (int i=0;i<30;++i) vis[t[i]]=1;
    while(q--) {
        char kind[10];
        int l,r,d;
        scanf("%s%d%d",kind,&l,&r);
        int lx=id[l],rx=id[r];
        if(*kind==‘a‘) {
            scanf("%d",&d);
            if(lx==rx) {
                for (int i=l;i<=r;++i) update(lx,a[i],d);
            }
            else {
                for (int i=l;i<=high[lx];++i) update(lx,a[i],d);
                for (int i=lx+1;i<rx;++i) add[i]+=d;
                for (int i=low[rx];i<=r;++i) update(rx,a[i],d);
            }
        }
        else {
            int ans=0;
            if(lx==rx) {
                for (int i=l;i<=r;++i) ans+=vis[a[i]+add[lx]];
            }
            else {
                for (int i=l;i<=high[lx];++i) ans+=vis[a[i]+add[lx]];
                for (int i=lx+1;i<rx;++i) {
                    for (int j=0;j<30;++j) if(add[i]<=t[j]) ans+=num[i][t[j]-add[i]];
                }
                for (int i=low[rx];i<=r;++i) ans+=vis[a[i]+add[rx]];
            }
            printf("%d\n",ans);
        }
    }
    return 0;
}

part four-Ynoi(不提供代码,不然没啥意思)

1.luogu5397 天降之物(第四分块)

题目大意:将所有x变成y,求最小的|i-j|使得a[i]==x && a[j]==y。

定义size[x]为x出现次数.

by lxl(写了一遍题解发现自己写得太垃圾了)

2. 五彩斑斓的世界。(第二分块)

题目大意:把区间>x的数减去x,求区间l,r内x的出现次数,值域1e5

解法:

提示了值域1e5,时间复杂度肯定与值域有关。

这题明显有一个性质,就是所有数的最大值总是单调不增的。

考虑利用这个性质

然后,他是要把l,r内>x的数-x,可一转化为把所有数-x,然后把<0的数+x

当最大值>=x*2时,把(1,x)合并到(x+1,x*2)

否则把(x+1,v)合并到(1,v-x)

这个用并查集维护即可。

所以神仙多多指教啊

---恢复内容结束---

原文地址:https://www.cnblogs.com/zjjandrew/p/11302466.html

时间: 2024-10-14 21:02:38

优秀数据结构1-分块(v1.0)的相关文章

如何成为优秀的程序员v1.0

什么是优秀的程序员? 1 你写的代码别人都能看明白. 2 有好奇心. 3 开发效率高. 4 善于利用网络解决常规问题. 5 追求完美. 怎么做到? 1 你写的代码别人都能看明白. 首先不能懒,最土的办法写一堆注释. 更好的办法,给每个类和方法起个好名字,让代码自己会说话. 问问你的小伙伴哪些地方他们看着费劲. 2 开发效率高. 善于利用开源工具. 重用以往的开发资源. 能够迅速拓宽作业面,让团队里的其他小伙伴能提前介入. 3利用网络解决常规问题.推荐一些优秀的国内外网站. http://www.

【转】寻找最好的笔记软件:三强篇(EverNote、Mybase、Surfulater) (v1.0) (

原文网址:http://blog.sina.com.cn/s/blog_46dac66f01000b57.html 寻找最好的笔记软件:三强篇(EverNote.Mybase.Surfulater) v1.0 作者:SuperboyAC 编译:xbeta 本系列还有:海选篇.三强篇.梦想篇.结论.篇外 通过上一篇<寻找最好的笔记软件:海选篇>的综合分析,作者发现有3种软件具有较明显的优势,可谓“笔记软件三强”.它们是:EverNote.Mybase 和 Surfulater.此三者相同之间差异

【工具篇】利用DBExportDoc V1.0 For MySQL自动生成数据库表结构文档

对于DBA或开发来说,如何规范化你的数据库表结构文档是灰常之重要的一件事情.但是当你的库,你的表排山倒海滴多的时候,你就会很头疼了. 推荐一款工具DBExportDoc V1.0 For MySQL(MySQL数据库表结构导出器). 介绍: DBExportDoc V1.0 For MySQL是一款利用office特性VBA开发的小工具,利用此工具可以将本机MySQL上任意数据库.任意表的数据结构导出.但是它不是一款你安装是一路next到finish就可以立刻使用的. 要注意的有两点: 首先,你

关于Farseer.Net V1.0 概念版本的消息

V0.2版的开源距离今天(05年03月)已有近3年的时间.可以说这个版本已经有点落伍的感觉了,呵呵. V0.2版至今一直处于BUG的修复及一些细小功能的增加,所以版本号上一直没有变化. 其实在这1.2年中,我一直在想着Farseer.Net 的未来发展状况.有尝试用EF的核心.也有想过用NHibernate的核心.仅仅是在这些核心的基础下做二次开发,以个人编码的经验从客户端调用角度进行“优化”,但总是感觉缺少点什么?没错,就是缺少研发精神,缺少属于Farseer.Net独特的一面,有种寄人(第三

【资源共享】Rockchip I2C 开发指南 V1.0

2C设备的设备应用非常广泛,常见的包含重力传感器,触摸屏驱动芯片,音频解码等 这个文档是RK3399的I2C开发文档:<Rockchip I2C 开发指南 V1.0> 内容预览: 下载地址:http://developer.t-firefly.com/thread-12495-1-1.html

Windows环境下Android Studio v1.0安装教程

Windows环境下Android Studio v1.0安装教程 Windows环境下Android Studio v1.0安装教程 准备工具 JDK安装包. 要求:JDK 7以及以上版本. Android Studio安装文件. Windows: exe(包含SDK) (813 MB) exe(不包含SDK) (250 MB) zip (235 MB) Mac dmg (234 MB) zip (233 MB) Linux: zip (233 MB) 说明: 32位系统和64位系统是同一个安

Alien.Skin.Bokeh.v1.0.3.Incl.

Ablume.Surfmemo.v4.2.Regged-WaLMaRT\ Alien.Skin.Blow.Up.v2.0.3.MacOSX.Incl.  Alien.Skin.Blow.Up.v2.0.4.Incl. TEL:15108931101   QQ:316859986 Alien.Skin.Exposure.v3.0.0.Incl.  Alien.Skin.Exposure.v3.0.0.MacOSX.Incl.  Alien.Skin.Eye.Candy.v6.0.0a.Incl. 

wzplayer for ios 针对(mms)优化版本V1.0

wzplayer for ios针对mms优化版本发布. 1.支持mms,http,rtmp,rtsp等协议 2.支持全格式 下载地址:http://www.coolradio.cn/WzPlayer.ipa 更强大的,请使用tlplayerhttp://blog.csdn.net/tigerleap/article/details/19007057 联系方式:[email protected] QQ:514540005 版权所有,禁止转载. 发布自:http://blog.csdn.net/t

Nat.Geo.Games.DogTown.v1.0.Cracked-F4C

Macaraja.Soft.RamWizard.v3.1.2.2.Cracked-QUANTiZE\ MetaProducts.LightPad.v4.6.164.WinAll.Incl.Keygen-CRD\ MetaProducts.Portable.Offline.Browser.v5.8.3158.Incl.Keymaker.And.Patch-ROGUE\ Millionenjagd.GERMAN-ALiAS\ Mishap.An.Accidental.Haunting-OUTLAWS