1. 程式人生 > 其它 >AtCoder Beginner Contest 223 題解

AtCoder Beginner Contest 223 題解

線段樹是一種用於區間更新,單點更新,區間查詢的一種演算法。
比如:區間染色,區間求和(也可用樹狀陣列),區間最值(也可用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;
}