线段树、树状数组

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;
}

  

猜你喜欢

转载自www.cnblogs.com/shirlybaby/p/12393272.html
今日推荐