线段树

线段树是一种用于区间更新,单点更新,区间查询的一种算法。
比如:区间染色,区间求和(也可用树状数组),区间最值(也可用RMQ算法,但RMQ为数值不可更新)
主要思路是,t[1]为根节点,t[k<<1],t[k<<1|1] 为 t[k] 的左右节点 t[k]的值为节点的值
如果一个数组长度为N,那么以t的长度最长为N*4。

以求最值为例
初始化

#define MAXN 10000
int t[MAXN<<2];
int va[MAXN];//原始数值
int lazy[MAXN<<2];

void pushUp(int k){
  t[k] = max(t[k<<1],t[k<<1|1]);
}

void build(int l,int r,int k){
  if ( l==r ){
    t[k] = va[l];
  }else{
    int mid = l+((r-l)>>1);
    build(l,mid,k<<1);
    build(mid+1,r,k<<1|1);
    pushUp(k);
  }
}

单点更新

void update(int p,int av,int l,int r,int k){
  if ( l==r ){
     t[k] += av;//此时 p==l && p==r 但是跟k不相等
     va[k] += av;
  }else{
    int mid = l+((r-l)>>1);
    if ( p <= mid ){
      update(p,av,l,mid,k<<1);
    }else{
      update(p,av,mid+1,r,k<<1|1);
    }
    pushUp(k);//更新了子树,当然要更新当前节点。
  }
}

区间查询

int query(int L,int R,int l,int r,int k){
  if ( L<= l && r <= R ){//当小于的时候,是返回给父节点,进行求max运算的,所以也是返回t[k] 即可
     return t[k];
  }
  int ret = INT_MIN;
  int mid = l+((r-l)>>1);
  if ( L <= mid ){//需要查询的区间被左子树部分包含
     ret = max(ret,query(L,R,l,mid,k<<1);
  }
  if ( mid < R ) {//需要查询的区间被左子树部分包含
    ret = max(ret,query(L,R,mid+1,r,k<<1|1);
  }
  return ret;
}

区间更新
假设,现在要更新所有数组值,都+3,那么,如果按非常朴素的想法来看,那么时而要个改整个线段树,这样也太不高效了,所以引入了一个lazy
当真正需要值时,才把子树的值变化。
lazy[k] = ? 是指,作为t[k] 已经更新过了,但是t[k<<1] t[k<<1|1] 还没更新过
所以,当清除lazy[k]的时候,需要把t[k<<1] t[k<<1|1] 也设置一下

void pushDown(int k){
  if ( lazy[k] > 0 ){
    t[k<<1] += lazy[k];
    t[k<<1|1] += lazy[k];
    lazy[k<<1] += lazy[k];
    lazy[k<<1|1] += lazy[k];
  }
}
void updateRange(int L,int R,int av,int l,int r,int k){
  if ( L<=l && r<=R ){
     t[k] += av;
     lazy[k] += av;//注意这里是+=,也就是如果之前有lazy没有完成,那么也是会有效的
  }else{
    pushDown(k);//如果子树还没有被更新到,那么是需要更新的
    int mid = l+((r-l)>>1);
    if ( L <= mid )
      updateRange(L,R,av,l,mid,k<<1);
    if ( mid < R)
      updateRange(L,R,av,mid+1,r,k<<1|1);
    
    pushUp(k);//更新了子树,当然要更新当前节点。
  }
}
//query也需要更改
int queryRange(int L,int R,int l,int r,int k){
  if ( L <= l && r <= R ){
    return t[k];
  }
  int ret = INT_MIN;
  int mid = l+((r-l)>>1);
  if ( L <= mid ){
    ret = max(ret,queryRange(L,R,l,mid,k<<1);
  }
  if ( mid < R ){
    ret = max(ret,queryRange(L,R,mid+1,r,k<<1|1);
  }
  return ret;
}

附poj2777 区间染色的ac代码

#include <iostream>
#include <cassert>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <cmath>
#include <climits>
#include <functional>
#include <list>
#include <cstdlib>
#include <set>
#include <stack>
#include <map>
#include <algorithm>

#define ll long long
using namespace std;

#define MAXN 100010

int t[MAXN<<2];
int lazy[MAXN<<2];
int va[MAXN];

void pushUp(int k){
	t[k] = t[k<<1] | t[k<<1|1];
}

void pushDown(int k){
	if ( lazy[k] > 0 ){
		t[k<<1] = lazy[k];
		t[k<<1|1] = lazy[k];
		lazy[k<<1] = lazy[k];
		lazy[k<<1|1] = lazy[k];
		lazy[k]  = 0;
	}
}

void build(int l,int r,int k){
	if ( l == r ){
		t[k] = va[l];
	}else{
		int mid = l+((r-l) >> 1);
		build(l,mid,k<<1);
		build(mid+1,r,k<<1|1);
		pushUp(k);
	}
}

void update(int L,int R,int v,int l,int r,int k){
	if ( L <= l && r <= R ){
		t[k] = v;
		lazy[k] = v;
	}else{
		pushDown(k);
		int mid = l+((r-l) >> 1);
		if ( L <= mid ){
			update(L,R,v,l,mid,k<<1);
		}
		if ( mid < R ){
			update(L,R,v,mid+1,r,k<<1|1);
		}
		pushUp(k);
	}
}

int query(int L,int R,int l,int r,int k){
	if ( L <= l && r <= R ){
		return t[k];
	}
	pushDown(k);
	int mid = l+((r-l) >> 1);
	int ret = 0;
	if ( L <= mid ){
		ret |= query(L,R,l,mid,k<<1);
	}
	if ( mid < R ){
		ret |= query(L,R,mid+1,r,k<<1|1);
	}
	return ret;
}

int getCnt(int x){
	int sum = 0;
	while( x > 0 ){
		x = x&(x-1);
		++sum ;
	}
	return sum;
}

int main(){
	int L,T,O;
	scanf("%d%d%d",&L,&T,&O);
	for( int i=0;i<=L+9;++i ){
		va[i] = 1;
	}
	build(1,L,1);
	char c;
	int A,B,C;
	for( int i=0;i<O;++i ){
		scanf("%c",&c);
		scanf("%c",&c);
		if ( c == 'C' ){
			scanf(" %d %d %d",&A,&B,&C);
			int a = min(A,B);
			int b = max(A,B);
			update(a,b,1<<(C-1),1,L,1);
		}else{
			scanf(" %d %d",&A,&B);
			int a = min(A,B);
			int b = max(A,B);
			int ret = query(a,b,1,L,1);
			printf("%d\n",getCnt(ret));
		}
	}
	return 0;
}

poj2828ac代码

#include <iostream>
#include <cassert>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <cmath>
#include <climits>
#include <functional>
#include <list>
#include <cstdlib>
#include <set>
#include <stack>
#include <map>
#include <algorithm>

#define ll long long
using namespace std;

#define MAXN 200010

int t[MAXN<<2];
int p[MAXN];
int v[MAXN];
int idxValue[MAXN];

void pushUp(int k){
	t[k] = t[k<<1] + t[k<<1|1];
}

void build(int l,int r,int k){
	if ( l==r ){
		t[k] = 1;
	}else{
		int mid = l+((r-l)>>1);
		build(l,mid,k<<1);
		build(mid+1,r,k<<1|1);
		pushUp(k);
	}
}

//表示,在求得cnt
void insert(int value,int cnt,int l,int r,int k){
	if ( l == r ){
		t[k] = 0;
		idxValue[l] = value;
		return;
	}
	int mid = l+((r-l)>>1);
	int leftCnt = t[k<<1];
	if (leftCnt >= cnt ){
		insert(value,cnt,l,mid,k<<1);
	}else{
		insert(value,cnt-leftCnt,mid+1,r,k<<1|1);
	}
	pushUp(k);
}

int main(){
	int N;
	while(~scanf("%d",&N)){
		memset(idxValue,0,sizeof(int)*(N+1));
		memset(t,0,sizeof(int)*((N+1)<<2));
		build(1,N,1);
		for( int i=1;i<=N;++i )scanf("%d%d",&p[i],&v[i]);
		for( int i=N;i>0;--i )insert(v[i],p[i]+1,1,N,1);
		for( int i=1;i<=N;++i) printf("%d ",idxValue[i]);
		printf("\n");
	}
	return 0;
}
posted @ 2021-10-22 11:07  传说中的水牛  阅读(25)  评论(0编辑  收藏  举报