数据结构(终极线段树篇)
摘要:
问题的提出:如何解决多样化的区间操作问题?
solve:线段树!!!
关键字:
线段树,可持久化线段树,权值线段树,线段树森林,动态开点线段树,区间操作,线段树应用。
前言:
区间操作问题的解决方法极多,如:树状数组,RMQ等。
所有这些数据结构都具有一定的局限性,都具有各自的优势和劣势。
而线段树无疑是这些数据结构中性价比最高的数据结构!(
Ps:搞什么线段树,暴力出奇迹啊!)
线段树,线段树,线段树。
顾名思义:树上每一个节点表示一个线段(区间),每一次对一整个线段(区间)进行操作,棒棒棒!!!
第1节讲述了线段树的基本性质。
第2节实现了基本线段树。
第3节讲述动态开点线段树。
//第4节讲述权值线段树。
//第5节讲述线段树森林。
//第6节讲述线段树的重要应用和例题。
1.线段树的基本性质:
1.0基本线段树的性质:
1.每个节点u表示一个区间[l,r]。若节点u非叶子节点,则u有两个儿子。设(mid=(l+r)/2)。 左儿子为[l,mid],节点编号为u*2。 右儿子为[mid+1,r],节点编号为u*2+1。
2.线段树的深度为O(lgn)
3.线段树的节点个数为O(n),常数约为4。
4.线段树上任意一个区间[l,r]都可以被O(logn)个线段覆盖。
思考:为何左儿子不为[l,mid+1],为何左儿子的节点编号为u*2。
思考:如何证明线段树的节点个数为O(n),且常数为4。
思考:如何证明线段树上任意一个区间[l,r]都可以被O(logn)个线段覆盖。
倘若我们只需要单点修改,我们只需要找到相应的叶子节点,更改叶子节点并沿路更新。
但倘若我们需要区间修改,我们却需要引入lazy tag(懒惰标记)。
每一次操作都先下放懒标记,清空标记,再进行相应的操作。
如上图:若将[1,5]全加上3。
需要标记tag[1,5]=3,下一次操作到[1,5]时下放。
tag[1,3]=3,tag[4,5]=3,tag[1,5]=0,sum[1,5]+=3*5;
2.基本线段树的实现:(建议读者自行思考)
2.1基本线段树的区间加,询问区间和:
#include<bits/stdc++.h> #define ll long long using namespace std; const int MAXN=1000005; ll a[MAXN]; ll read() { char c=getchar(); ll f=1,x=0; while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();} while (isdigit(c)) {x=x*10+c-'0'; c=getchar();} return f*x; } struct Segment_tree { struct node { int l,r; ll sum,tag; } tree[MAXN<<2]; void up(int x){ tree[x].sum=tree[x<<1].sum+tree[(x<<1)|1].sum; } void down(int x) { int num1=(tree[x<<1].r-tree[x<<1].l+1); int num2=(tree[(x<<1)|1].r-tree[(x<<1)|1].l+1); tree[(x<<1)|1].sum+=tree[x].tag*num2; tree[x<<1].tag+=tree[x].tag; tree[(x<<1)|1].tag+=tree[x].tag; tree[x<<1].sum+=tree[x].tag*num1; tree[x].tag=0; } void build(int x,int l,int r,ll *a) { tree[x].l=l; tree[x].r=r; if (l==r) { tree[x].sum=a[l]; tree[x].tag=0; return; } int mid=(l+r)>>1; build(x<<1,l,mid,a); build((x<<1)|1,mid+1,r,a); up(x); } void change(int x,int l,int r,ll y) { if (tree[x].l>=l&&tree[x].r<=r) { tree[x].tag+=y; tree[x].sum+=y*(tree[x].r-tree[x].l+1); return; } down(x); int mid=(tree[x].l+tree[x].r)>>1; if (r<=mid) change(x<<1,l,r,y); else if (l>mid) change((x<<1)|1,l,r,y); else { change(x<<1,l,mid,y); change((x<<1)|1,mid+1,r,y); } up(x); } ll query(int x,int l,int r) { if (tree[x].l>=l&&tree[x].r<=r) return tree[x].sum; down(x); int mid=(tree[x].l+tree[x].r)>>1; if (r<=mid) return query(x<<1,l,r); else if (l>mid) return query((x<<1)|1,l,r); else return query(x<<1,l,mid)+query((x<<1)|1,mid+1,r); } void print(int x) { for (int i=1;i<=x;i++) printf("%d %d %d\n",tree[i].l,tree[i].r,tree[i].sum); printf("\n"); } } segment_tree; int main() { int n=read(),m=read(); for (int i=1;i<=n;i++) a[i]=read(); segment_tree.build(1,1,n,a); for (int i=1;i<=m;i++) { int c=read(); if (c==1) { int x=read(),y=read(),z=read(); segment_tree.change(1,x,y,z); } else { int x=read(),y=read(); printf("%lld\n",segment_tree.query(1,x,y)); } } return 0; }
3.动态开点线段树
动态开点线段树,实现单点加,区间求和。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int MAXN=1000005; const int MAXS=1e9;
ll read()
{
char c=getchar(); ll f=1,x=0;
while (!isdigit(c)) {if (c=='-') f=-1; c=getchar();}
while (isdigit(c)) {x=x*10+c-'0'; c=getchar();}
return f*x;
}
struct Dynamic_segment_tree
{
int nodenum=0;
struct node
{
int l,r,leftnode,rightnode;
ll sum;
} tree[MAXN<<2];
void change(int &x,int l,int r,int t,ll y)
{
if (!x)
{
nodenum++;
x=nodenum;
}
tree[x].l=l;
tree[x].r=r;
tree[x].sum+=y;
if (l==r) return;
int mid=(tree[x].l+tree[x].r)>>1;
if (t<=mid) change(tree[x].leftnode,l,mid,t,y);
else change(tree[x].rightnode,mid+1,r,t,y);
}
ll query(int x,int l,int r)
{
if (!x) return 0;
if (tree[x].l>=l&&tree[x].r<=r) return tree[x].sum;
int mid=(tree[x].l+tree[x].r)>>1;
if (r<=mid) return query(tree[x].leftnode,l,r);
else if (l>mid) return query(tree[x].rightnode,l,r);
else return query(tree[x].leftnode,l,mid)+query(tree[x].rightnode,mid+1,r);
}
void print(int x)
{
for (int i=1;i<=x;i++)
printf("%d %d %d %d %d\n",tree[i].l,tree[i].r,tree[i].leftnode,tree[i].rightnode,tree[i].sum);
printf("\n");
}
} dynamic_segment_tree;
int main()
{
int n=read(),m=read(),root=0;
for (int i=1;i<=m;i++)
{
int c=read(),x=read(),y=read();
if (c==1) dynamic_segment_tree.change(root,1,MAXS,x,y);
else printf("%d\n",dynamic_segment_tree.query(root,x,y));
//dynamic_segment_tree.print(20);
}
return 0;
}
之后的内容将在7月前全部完成。