線段樹

1、中心思想

二分, 每查找一個區間或者修改一個區間的時候,一層一層往下二分,找到

區間的左端點和右端點是否是在應該修改的區間內,而後進行修改操做...

2、基本操做

一、建樹

首先你要弄明白這棵線段樹是幹什麼的,即這棵線段樹的節點要存儲哪些信息

若是你不知道線段樹裏要存儲哪些信息,回答如下三個問題:

①對於要求的輸出,須要維護哪些信息(線段樹節點裏要存什麼)?
②這些信息是否能夠直接獲得?
③若是不能,須要維護哪些信息來間接得到?node

而後咱們要作的事情就是:

兩件事:由上而下幹什麼? 由下而上幹什麼?
由上而下的過程——初始化
由下而上的過程——合併區間信息
從上往下跑一圈又必需要倒回去的算法——遞歸。ios

那麼就是遞歸實現的嘍。git

要開四倍空間

來自沙雕網友的解答:

理論上是2n-1的空間,可是你遞歸創建的時候當前節點爲r
,那麼左右孩子分別是2r,2r+1,此時編譯器並不知道遞歸已結束,
由於你的結束條件是在遞歸以前的,因此編譯器會認爲下標訪問出錯,也就是空間開小了,
應該再開大2倍。有時候可能你發現開2,3倍的空間也能夠AC,那只是由於測試數據並無那麼大。算法

主要的思路就是遞歸加二分
看看代碼吧(~~懷念那學長看着我打代碼,強行改我碼風的日子)測試

void build(ll l,ll r,ll rt) {
	t[rt].len = r - l + 1;
	if(l == r) {
		t[rt].sum = read();
		return;
	}
	ll m = (l + r) >> 1;
	build(l, m, lson);
	build(m + 1, r ,rson);
	pushup(rt);
}

單點查詢

知道二分與遞歸以後,單點查詢那不就成了一個,二分求解的過程了嘛ui

void ask(int k)
{
    if(tree[k].l==tree[k].r) //當前結點的左右端點相等,那麼說明是葉子節點,是最終答案 
    {
        ans=tree[k].w;
        return ;
    }
    int m=(tree[k].l+tree[k].r)/2; // 二分查找
    if(x<=m) ask(k*2);
    else ask(k*2+1);
}

單點修改

與單點查詢沒有什麼區別,就是加了修改操做
還有一個回溯的過程this

void add(int k)
{
    if(tree[k].l==tree[k].r)//找到目標位置 
    {
        tree[k].w+=y;
        return;
    }
    int m=(tree[k].l+tree[k].r)/2;
    if(x<=m) add(k*2);
    else add(k*2+1);
    tree[k].w=tree[k*2].w+tree[k*2+1].w;//全部包含結點k的結點狀態更新 
}

懶標記

由於線段樹的時候,有一些節點雖然咱們下放了值了,也加上了,可是咱們並無用到這個地方
那麼咱們能夠如今查詢到的地方打上一個lazy標記(稱爲懶標記),而後每次查到這裏的時候
就將當前的區間維護的值,與lazy標記處理一下就行了,這樣咱們就沒有必要再向下跑到葉子節點
而後在合併區間信息了,很麻煩,也很慢...spa

區間修改

當他區間修改的時候,無非就是三種狀況code

一、當你查到的區間與須要修改的區間這樣時

like this
blog

由於中心思想是二分,那麼咱們能夠求出當前查到的區間的mid而後與

須要修改的區間的l和r進行比較,而後繼續遞歸。

二、當你須要修改的區間在你當前查詢的區間中時

like this

按照上邊的思路,咱們要取查詢到的區間的兩邊段點,而後按照mid的狀況更新

三、當你查詢到的區間徹底在須要修改的區間內時

like this

由於徹底在區間內了,因此直接修改就行了

看一下代碼吧

void updata(ll L,ll R,ll c,ll l,ll r,ll rt){
	if(L <= l && r <= R){
		t[rt].sum += t[rt].len * c;
		t[rt].lazy += c;
		return ;
	}
	pushdown(rt);
	ll m = (l + r) >> 1;
	if(L <= m) updata(L,R,c,l,m,lson);
	if(R > m) updata(L,R,c,m + 1,r,rson);
	pushup(rt);
}

四、區間查詢

和上邊的差很少,狀況也是上邊的三種狀況,而後這樣咱們能夠去一個ans而後累加就好了

ll query(ll L,ll R,ll l,ll r,ll rt){
	if(L <= l && r <= R) return t[rt].sum;
	pushdown(rt);
	ll m = (l + r) >> 1,ans=0;
	if(L <= m) ans+=query(L, R, l, m, lson);
	if(R > m) ans+=query(L, R, m + 1, r, rson);
	return ans;
}

最後合併總的代碼(由於單點查詢與單點修改能夠仿照區間查詢與區間修改上來我就不放了)

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<set>
#include<vector>
#include<string>
#define ll long long
#define N 1000010
#define lson rt << 1
#define rson rt << 1 | 1

using namespace std;
ll n,m,rt;
struct node {
	ll sum,lazy,len;
} t[N << 2 | 1];
ll read() {
	ll s=0,f=0;
	char ch=getchar();
	while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
	while(isdigit(ch)) s=s*10+(ch^48),ch=getchar();
	return f?-s:s;
}
void build(ll l,ll r,ll rt);
void pushup(ll rt);
void pushdown(ll rt);
void updata(ll L,ll R,ll c,ll l,ll r,ll rt);
ll query(ll L,ll R,ll l,ll r,ll rt);

int main() {
	n = read(), m = read();
	build(1,n,1);
	for(ll i = 1, opt, l, r, k; i <= m; i++){
		opt = read(),l = read(),r = read();
		if(opt == 1){k = read();updata(l,r,k,1,n,1);}
		else printf("%lld\n",query(l,r,1,n,1));
	}
	return 0;
}

void build(ll l,ll r,ll rt) {
	t[rt].len = r - l + 1;
	if(l == r) {
		t[rt].sum = read();
		return;
	}
	ll m = (l + r) >> 1;
	build(l, m, lson);
	build(m + 1, r ,rson);
	pushup(rt);
}

void pushup(ll rt) {
	t[rt].sum = t[lson].sum + t[rson].sum;
}

void pushdown(ll rt) {
	if(t[rt].lazy) {
		t[lson].lazy += t[rt].lazy;
		t[rson].lazy += t[rt].lazy;
		t[lson].sum += t[lson].len * t[rt].lazy;
		t[rson].sum += t[rson].len * t[rt].lazy;
		t[rt].lazy = 0;
	}
}

void updata(ll L,ll R,ll c,ll l,ll r,ll rt){
	if(L <= l && r <= R){
		t[rt].sum += t[rt].len * c;
		t[rt].lazy += c;
		return ;
	}
	pushdown(rt);
	ll m = (l + r) >> 1;
	if(L <= m) updata(L,R,c,l,m,lson);
	if(R > m) updata(L,R,c,m + 1,r,rson);
	pushup(rt);
}

ll query(ll L,ll R,ll l,ll r,ll rt){
	if(L <= l && r <= R) return t[rt].sum;
	pushdown(rt);
	ll m = (l + r) >> 1,ans=0;
	if(L <= m) ans+=query(L, R, l, m, lson);
	if(R > m) ans+=query(L, R, m + 1, r, rson);
	return ans;
}
相關文章
相關標籤/搜索