RMQ问题:区间最大值或最小值
操作:求区间最值、修改元素
区间和问题:修改操作在求和
线段树:用于区间处理的数据结构,二叉树构建,当查找点或者区间的时候,顺着节点往下找,最多log2n次就能找到,用了二叉树折半查找
!修改和查询可以用一起做,所以复杂度是O(mlog2n),m次操作
点修改:
poj last cows
方法1:暴力O(N^2)
#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;
//线段树
//知道排在他前面的数比它小的有多少个
int pre[8100];
int ans[8100];
int num[8100];
//剩下的编号中pre[n]+1大的编号就是ans[n]
//暴力:从pre的末尾开始算,没处理完一头牛,就需要重新排序,重新拍的时候可以做下一次查找,所以复杂度为O(n^2)
int n;
int main(){
scanf("%d",&n);
pre[1]=0;
for(int i=1;i<=n;i++) num[i]=i;
for(int i=2;i<=n;i++) scanf("%d",&pre[i]);
for(int i=n;i>=1;i--){
int k=0;
for(int j=1;j<=n;j++){
if(num[j]!=-1) k++;
if(k==pre[i]+1){
ans[i]=num[j];
num[j]=-1;
break;
}
}
}
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
方法2:线段树
#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=10000;
const int INF=0x3fffffff;
typedef long long LL;
//存储空间:4*n
//复杂度:
//线段树吧n个数按照二叉树进行分组,每次更新有关节点时,这个节点下面的所有子节点都隐含被更新了,从而减少了操作次数
struct node{
int l,r;
int len;
}tree[4*maxn];
void build(int left,int right,int y){
tree[y].l=left;
tree[y].r=right;
tree[y].len=right-left+1;
if(left==right) return;
build(left,(left+right)>>1,y<<1); //左子树
build(((left+right)>>1)+1,right,(y<<1)+1);
}
int que(int u,int num){ //查询+维护:所求为当前区间中坐起第num个元素
tree[u].len--; //对方问到的区间都进行处理
//如果找到了(叶子)
if(tree[u].l==tree[u].r) return tree[u].l;
if(tree[u<<1].len<num) {
return que((u<<1)+1,num-tree[u<<1].len); //左子区间个数不够 ,查询第右区间第 num-tree[u<<1].len个元素
}
if(tree[u<<1].len>=num) return que(u<<1,num); //左子区间够,就往左查
}
int pre[maxn];
int ans[maxn];
int main(){
int n;
scanf("%d",&n);
pre[1]=0;
for(int i=2;i<=n;i++) scanf("%d",&pre[i]);
build(1,n,1);
for(int i=n;i>=1;i--){ //从后往前推出每次最后一位数字
ans[i]=que(1,pre[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=10000;
const int INF=0x3fffffff;
typedef long long LL;
//用完全二叉树实现线段树
int pre[maxn],ans[maxn],tree[maxn*4];//tree村的是数量
int n;
void build(int n,int last_left){
int i;
for(i=last_left;i<last_left+n;i++) tree[i]=1;
//从二叉树的最后一行倒推到根节点,根节点的值是牛的数量
while(last_left!=1){
for(i=last_left/2;i<last_left;i++) tree[i]=tree[i*2]+tree[i*2+1];
last_left=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) { //左子区间个数不够了,查询右子区间坐起第num-tree[u<<1].len个元素
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(){
scanf("%d",&n);
pre[1]=0;
for(int i=2;i<=n;i++) scanf("%d",&pre[i]);
int last_left=1<<(int(log(n)/log(2))+1); //二叉树最后一行的最左边的一个,计算方法是找到离2最近的2的指数
build(n,last_left);
for(int i=n;i>=1;i--){
ans[i]=que(1,pre[i]+1,last_left)-last_left+1;
}
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
区间修改:
加:把区间ai.....aj的值全部加上v
查询:查询L,R之间所有的和
lazy_tag方法:当修改的是一个整块的区间时,只对这个区间进行整体上的修改,其内部元素不需要改变,但是当这部分线段的一致性被破坏时,才会把变化值传递给子区间
查询也是一样
有错
#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){
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){
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;
}
树状数组BIT
利用二进制特征进行检索的树状结构
lowbit运算:找到x的二进制数的最后1个1
#define lowbir(x) ((x)&-(-x))
void add(int x,int d){ //更新数组tree[]
while(x<=n){
tree[x]+=d;
x+=lowbit(x);
}
}
//求和
int summ(int x){
int ans=0;
while(x>0){
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
tree[x]就是x前面lowbit(x)个数相加的结果
再次计算2182题,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=10010;
const int INF=0x3fffffff;
typedef long long LL;
#define lowbit(x) ((x)&(-x))
int tree[maxn],pre[maxn],ans[maxn];
int n;
void add(int x,int d){ //更新数组tree[]
while(x<=n){
tree[x]+=d;
x+=lowbit(x);
}
}
//求和
int summ(int x){
int ans=0;
while(x>0){
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
int findpos(int x){ //寻找sum(x)=pre[i]+1所对应的x,就是第x头牛
int l=1,r=n;
while(l<r){
int mid=(l+r)>>1;
if(summ(mid)<x) l=mid+1;
else r=mid;
}
return l;
}
int main(){
scanf("%d",&n);
pre[1]=0;
for(int i=2;i<=n;i++){
scanf("%d",&pre[i]);
}
for(int i=1;i<=n;i++){
tree[i]=lowbit(i); //这个题目特殊,不需要add初始化,直接用lowbit就可以了
}
for(int i=n;i>=1;i--){
int x=findpos(pre[i]+1);
add(x,-1); //更新tree数字,减少一个
ans[i]=x;
}
for(int i=1;i<=n;i++) printf("%d\n",ans[i]);
return 0;
}
posted on
浙公网安备 33010602011771号