线段树
线段树是一种用于区间更新,单点更新,区间查询的一种算法。
比如:区间染色,区间求和(也可用树状数组),区间最值(也可用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;
}