RMQ问题:区间最大值或者最小值问题,类似的还要区间和问题
操作:
(1)求最值 :区间内
(2)修改元素 :点修改
线段树:用于区间处理的数据结构,用二叉树构造
复杂度:O(nlogn):二叉折半查找
查找点或者区间的时候:顺着往下查找
修改元素:直接修改叶子节点,然后自底向上更新
!!!存储空间:4n
更新和查询:更新经过的每个节点,就把这个节点剩下的数量(长度)减1
复杂度:线段是把n个数按照二叉树进行分组,每次更新有关节点的时候,这个节点下面的所有子节点都隐含被更新了,从而减少了操作次数
例题:
还是last cows
第一种做法:用结构体实现线段树
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=1010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//线段树做法
/*
从后往前遍历输入的序列,遇到的每个值a表示此牛在剩余牛中排在第a+1个,删除此编号,循环此过程,最终得到的序列即为牛在此队列中的编号序列。
借助线段树查找未删除的数中排在第a+1个位置(编号排序位置)的牛的位置(读取顺序)
*/
struct node{
int l,r,len;
}cow[100000];
int s[100000],ans[100000];
void build(int v,int l,int r){
cow[v].l=l;
cow[v].r=r;
cow[v].len=r-l+1;
if(l==r) return;
int mid=(l+r)/2;
build(v*2,l,mid);
build(v*2+1,mid+1,r);
}
int que(int v,int k){
--cow[v].len;
if(cow[v].l==cow[v].r) return cow[v].r;
//找到叶子节点, 注意此处不可用cow[v].len == 0代替,否则单支情况将直接返回,导致未达到最末端
else if(cow[v*2].len>=k){
return que(v*2,k);
}
else return que(v*2+1,k-cow[v*2].len);////!!!!
}
int main(){
int n;
while(~scanf("%d",&n)){
for(int i=2;i<=n;i++) scanf("%d",&s[i]);
s[1]=0;
build(1,1,n);
for(int i=n;i>=1;i--){
ans[i]=que(1,s[i]+1);
}
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
}
return 0;
}
第二种做法
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
const int maxn=11010;
const int INF=0x3fffffff;
typedef long long LL;
typedef unsigned long long ull;
//数组实现线段树
int n;
int pre[maxn],tree[maxn*4]={0},ans[maxn]={0};
void build(int n,int last_left){
for(int i=last_left;i<last_left+n;i++) tree[i]=1; //最后一行赋值
//从二叉树的最后一行倒推到根节点,根节点的值是牛的总数
while(last_left!=1){
for(int i=last_left/2;i<last_left;i++) tree[i]=tree[i*2]+tree[i*2+1];
last_left/=2;
}
}
int que(int u,int num,int last_left){ //查询+维护,求出当前区间中坐起第num个元素
tree[u]--;
if(tree[u]==0&&u>=last_left) return u;
if(tree[u<<1]<num) //左子区间数量不够,查到右子区间
return que((u<<1)+1,num-tree[u<<1],last_left);
if(tree[u<<1]>=num) //左子区间数量够了
return que(u<<1,num,last_left);
}
int main(){
int las;
scanf("%d",&n);
pre[1]=0;
for(int i=2;i<=n;i++) scanf("%d",&pre[i]);
las=1<<(int(log(n)/log(2))+1);
//cout<<las<<endl;
build(n,las); //从后往前退出每次最后一个数字
for(int i=n;i>=1;i--) ans[i]=que(1,pre[i]+1,las)-las+1;
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
当数据太大:也可以考虑离散化,把原有的大二叉树压缩为小二叉树,但是压缩前后子区间的关系不变
区间修改
操作:(1)加 (2)查询和
lazy_tag方法:当修改一个整块区间时,只对这个线段区间进行整体上的修改,其内部每个元素内容先不修改,只有当这部分线段的一致性被破坏时才把变化之传给子区间(查询时也一样)
tag[]数组:记录节点i是否用到lazy原理,其值是op a b c中的c,如果做了多次lazy,那么add[]可以累加,如果在某次操作中被深入, 破坏了lazy,那么add[]归0
POJ 3468 A Simple Problem with Integers
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#include<cstdio>
#include<queue>
#include<map>
#include<vector>
#include<set>
using namespace std;
//有错
const int maxn=1e5+5;
const int INF=0x3fffffff;
typedef long long LL;
LL summ[maxn<<2],add[maxn<<2]; //4倍空间
void pusup(int r){ //向上更新,把值给i递归到父节点 什么时候使用这个函数呢?当现在的节点发生变化,需要顺便改变父节点
summ[r]=summ[r<<1]+summ[r<<1|1];
}
void push_down(int rt,int m){ //更新r的子节点,m为长度
if(add[rt]){
add[rt<<1]+=add[rt];
add[rt<<1|1]+=add[rt];
summ[rt<<1]+=(m-(m>>1))*add[rt];
summ[rt<<1|1]+=(m>>1)*add[rt];
add[rt]=0; //取消本层的标记
}
}
void build(int l,int r,int rt){ //用满二叉树建树
add[rt]=0;
if(l==r) {
scanf("%lld",&summ[rt]); //叶子节点 赋值
return;
}
int mid=(l+r)/2;
build(l,mid-1,rt<<1);
build(mid+1,r,rt<<1|1);
pusup(rt); //这是个递归结构 向上更新区间和
}
void update(int a,int b,LL c,int l,int r,int rt){ //区间更新,
if(a<=l&&b>=r){ //lazy方法,包含在区间里面,就整体更新
summ[rt]+=(r-l+1)*c;
add[rt]+=c;
return;
}
push_down(rt,r-l+1); //向下更新
int mid=(l+r)/2;
//这里就是在处理有分叉的情况
if(a<=mid) update(a,b,c,l,mid-1,rt<<1); //分成两半,继续深入
if(b>mid) update(a,b,c,mid+1,r,rt<<1|1);
pusup(rt); //向上更新
}
LL que(int a,int b,int l,int r,int rt){ //区间求和
if(a<=l&&b>=r) return summ[rt]; //满足lazy直接返回
push_down(rt,r-l+1); //向下更新
int mid=(l+r)/2;
LL ans=0;
if(a<=mid) ans+=que(a,b,l,mid-1,rt<<1);
if(b>mid) ans+=que(a,b,mid+1,r,rt<<1|1);
return ans;
}
int main(){
int n,m;
scanf("%d %d",&n,&m);
build(1,n,1); //先建树
while(m--){
string str;
int a,b;
LL c;
cin>>str;
if(str[0]==‘C‘){
scanf("%d %d %lld",&a,&b,&c);
update(a,b,c,1,n,1);
}
else{
scanf("%d %d",&a,&b);
printf("%lld\n",que(a,b,1,n,1));
}
}
return 0;
}
原文:https://www.cnblogs.com/shirlybaby/p/12772698.html