博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
NOIP模板复习(4)区间操作之莫队算法,树状数组,线段树
阅读量:4611 次
发布时间:2019-06-09

本文共 4969 字,大约阅读时间需要 16 分钟。

NOIP模板复习(4) 区间操作之莫队算法,树状数组,线段树

目录


1.莫队算法

  莫队算法,顾名思义,是由前国家队队长莫涛神犇发明的区间离线查询算法。时间复杂度为\(O(n\sqrt{n})\),可以维护一些线段树不好维护或干脆没法维护信息。


1.1算法原理

  莫队算法被称为"优雅的暴力",其实就是利用询问的区间间的关系来做到降低指针无效移动次数的算法。具体是用分块和排序来实现对查询操作的维护。

  具体来讲就是先将原数列进行每块操作,再按查询的左端点所在的块和右端点排序。


1.2算法实现

  这里以询问区间内某一数字数量为例。

#include 
#include
#include
#include
#include
#include
using namespace std;int block[10005];int ans[10005];int num[10005];struct query{ int l,r,num,id; bool operator <(const query& mid)const { if(block[this->l]==block[mid.l]) { return this->r
l
ask[i].l) { l--; cnt[num[l]]++; } while(r>ask[i].r) { cnt[num[r]]--; r--; } ans[ask[i].id]=cnt[ask[i].num]; } for(int i=1;i<=m;i++) { cout<
<

2.树状数组

   树状数组是一种利用二进制原理维护区间信息的数据结构,可以在\(O(nlog_2(n))\)的时间内查询和修改,虽然其修改和查询的范围有限,但因为其实现简单和与线段树相比的较低的时间常数,让它在某些范围内经常使用。


2.1结构原理

  因为树状数组的结构原理较难理解,本人就不在这里献丑了(其实是懒),推荐阅读以下博客:。


2.2结构实现

  这里以区间加法和区间和为例。

#include 
#include
#include
#include
#include
#include
using namespace std;int num[10005];int tree[10005];char str[5];int n,m;inline int lowbit(int s){ return s&(-s);}void add(int pos,int nums){ for(int i=pos;i<=n;i+=lowbit(i)) { tree[i]+=nums; } return ;}int query(int pos){ int ans=0; for(int i=pos;i>0;i-=lowbit(i)) { ans+=tree[i]; } return ans;}int main(){ scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); add(i,num[i]); } for(int i=1;i<=m;i++) { scanf("%s",&str[1]); if(str[1]=='A') { int pos,addval; scanf("%d %d",&pos,&addval); add(pos,addval); } else if(str[1]=='Q') { int l,r; scanf("%d %d",&l,&r); cout<
<

3.线段树

  线段树,从名字就可以看出这是一种树形结构。其中每个节点表示一个区间的信息,同时线段是也是一个完全二叉树,通过二分的思想可以实现总共\(为O(nlog_2n)\)的时间复杂度,其中单个查询和修改的时间复杂度仅为\(O(log_2n)\)


3.1结构原理

  通过建立一个完全二叉树,将每个区间的信息进行递归式合并。通过将每个区间拆分为两段来实现高效的查询。同时,因为是一颗完全二叉树,所以可以在一个线性的数组中存放。


3.2单点修改+区间查询

  以单点加法和区间查询最大值为例。

#include 
#include
#include
#include
#include
#include
using namespace std;int tree[10005<<2];int num[10005];char str[5];void build(int root,int l,int r){ if(l==r) { tree[root]=num[l]; return ; } int mid=(l+r)>>1; build(root<<1,l,mid); build(root<<1|1,mid+1,r); tree[root]=max(tree[root<<1],tree[root<<1|1]); return ;}void updata(int root,int l,int r,int pos,int val){ if(l==r) { if(l==pos) { tree[root]+=val; } return ; } int mid=(l+r)>>1; if(pos<=mid) { updata(root<<1,l,mid,pos,val); } else { updata(root<<1|1,mid+1,r,pos,val); } tree[root]=max(tree[root<<1],tree[root<<1|1]);}int query(int root,int l,int r,int ql,int qr){ if(ql>r||qr
=l&&qr<=r) { return tree[root]; } int mid=(l+r)>>1; int lson=query(root<<1,l,mid,ql,qr); int rson=query(root<<1|1,mid+1,r,ql,qr); return min(lson,rson);}int main(){ int n,m; scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) { scanf("%d",&num[i]); } build(1,1,n); for(int i=1;i<=m;i++) { scanf("%s",&str[1]); if(str[1]=='A') { int pos,val; scanf("%d %d",&pos,&val); updata(1,1,n,pos,val); } else if(str[1]=='Q') { int l,r; scanf("%d %d",&l,&r); cout<
<

3.3Lazy标记

  因为单个节点的修改复杂度为\(O(log_2n)\)当遇到区间修改的时候,复杂度就会变得无法接受,因此引入了Lazy标记这个东西,当我们要进行修改的时候可以先将修改的信息记在Lazy标记中,直到查询时才将需要的区间的信息进行下传。这样可以在一定程度上降低无用操作的时间复杂度。


3.4Lazy标记实现

  以区间加法和区间最大值为例

#include 
#include
#include
#include
#include
#include
using namespace std;struct node{ int val,lazytag;};node tree[10005<<2];int num[10005];void pushdown(int root){ if(tree[root].lazytag) { tree[root<<1].lazytag+=tree[root].lazytag; tree[root<<1|1].lazytag+=tree[root].lazytag; tree[root<<1].val+=tree[root].lazytag; tree[root<<1|1].val+=tree[root].lazytag; tree[root].lazytag=0; } return ;}void build(int root,int l,int r){ tree[root].lazytag=0; if(l==r) { tree[root].val=num[l]; return ; } int mid=(l+r)>>1; build(root<<1,l,mid); build(root<<1|1,mid+1,r); tree[root].val=max(tree[root<<1].val,tree[root<<1|1].val); return ;}void updata(int root,int l,int r,int ql,int qr,int val){ if(ql>r||qr
=l&&qr<=r) { tree[root].val+=val; tree[root].lazytag+=val; return ; } pushdown(root); int mid=(l+r)>>1; updata(root<<1,l,mid,ql,qr,val); updata(root<<1|1,mid+1,r,ql,qr,val); tree[root].val=max(tree[root<<1].val,tree[root<<1|1].val); return ;}int query(int root,int l,int r,int ql,int qr){ if(ql>r||qr
=l&&qr<=r) { return tree[root].val; } pushdown(root); int mid=(l+r)>>1; int lson=query(root<<1,l,mid,ql,qr); int rson=query(root<<1|1,mid+1,r,ql,qr); return tree[root].val=max(tree[root<<1].val,tree[root<<1|1].val);}int main(){ int n,m; for(int i=1;i<=n;i++) { scanf("%d",&num[i]); } build(1,1,n); for(int i=1;i<=m;i++) { scanf("%s",&str[1]); if(str[1]=='A') { int l,r,val; scanf("%d %d %d",&l,&r,&val); updata(1,1,n,l,r,val); } else if(str[1]=='Q') { int l,r; scanf("%d %d",&l,&r); cout<
<

转载于:https://www.cnblogs.com/Heizesi/p/7787963.html

你可能感兴趣的文章
Nginx 假如reload或reopen时发生错误如何解决
查看>>
Linux网络配置方法(DNS,IP,GW)
查看>>
go语言基础之二维数组
查看>>
vim基本命令
查看>>
[转]HTTP协议之状态码详解
查看>>
box-shadow
查看>>
select * 和select 1 以及 select count(*) 和select count(1)的区别
查看>>
进度条04
查看>>
Silverlight RadGridView的HeaderCellStyle样式
查看>>
IE兼容CSS3圆角border-radius的方法
查看>>
Elsevier期刊投稿状态
查看>>
flask
查看>>
Heartbeat+LVS构建高可用负载均衡集群
查看>>
c++中const使用详解
查看>>
项目百态——深入理解软件项目行为模式(一)
查看>>
C# 文件读写Helper类
查看>>
Linux 命令总结
查看>>
BZOJ1386 : [Baltic2000]Stickers
查看>>
android联系人应用感悟
查看>>
js小作业
查看>>