1. 程式人生 > 實用技巧 >前端的一些小點

前端的一些小點

1.是啥

將一個大的區間平均分成兩個小的區間,就是分治的思想,最多\(O(logn)\)

2.能幹啥

線段樹的本質是把要求的東西一分為二,然後把它拼回去。由此可知,我們只能將線段樹用於區間修改,區間查詢的問題。注意:要滿足區間可加性。

3.怎麼實現

首先,我麼們看看怎樣建樹:

void build(int cur, int l, int r) { // l,r:當前的範圍  cur:當前的編號
	if(l == r) { // 如果TA是線段樹的葉子節點的話,我們就直接賦值
		sum[cur] = num[l];
		return;
	}
	int mid = (l + r) / 2;
	build(cur * 2, l, mid);//遞迴建樹
	build(cur * 2 + 1, mid + 1, r);
	pushup(cur); //奇怪的東西
}

這裡面好像混進去了一個奇怪的東西:pushup,這個函式是將當前節點所維護的區間和更新,就是把TA兩個兒子維護的值加起來,程式碼很簡單,就不放了

void pushup(int x) {sum[x] = sum[x * 2] + sum[x * 2 + 1];}

然後是查詢,讓我們康康:

int query(int cur, int x, int y, int l, int r) { //cur:當前的節點編號    x,y:要查詢的區間   l,r:當前的區間
	if(l > y || r < x) return 0;  //如果當前的區間完全不在要查詢的區間內,就不管TA。
	else if(l >= x && r <= y) return sum[cur];//榮國完全包含的話,直接返回TA維護的值
	else { //否則就是不完全包含,這時,我們要遞迴兩個孩子解決問題
		int mid = (l + r) / 2;
		pushdown(cur, l, r); // 奇怪的東西*2
		return query(cur * 2, x, y, l, mid) + query(cur * 2 + 1, x, y, mid + 1, r);
	}
}

這時,我們又發現了一個奇怪的東西,(這博主好垃圾),這又是幹嘛的呢?我們要引入一個非常高階(霧)的東西:

懶標記

這名字十分的LaJi,為什麼要這東西呢?學過分塊的童鞋可能知道這是在對一整個區間進行修改的時候,做的一個標記,記錄的是這個區間整體的改動情況,這樣就不用修改到每一個節點,在這個題中就是不需要修改到葉子節點,減少了遞迴的深度。

怎麼將一個節點打上懶標記呢?這時我們要做兩件事:

1.將懶標記數組裡面加上要修改的值。

2.將TA維護的區間和增加。

請看程式碼:

void AddTag(int cur, int val, int l, int r) {
	tag[cur] += val; // 標上懶標記
	sum[cur] += (r - l + 1) * val; // 增加區間和
}

話說回來,那個pushdown又是什麼呢?


pushdown---學名:懶標記下傳。

功能:將父親節點的懶標記傳給兒子節點。

用處:在要訪問一個節點的兒子時要做的事。

原因:修改的時候並沒有把那個子樹上的每個節點的sum值修改,我們又要用到
TA維護的值,所以要懶標記下傳。


大家聽懂了嗎?沒聽懂就直接copy程式碼去

知道了pushup和pushdown之後,修改這個操作就很簡單啦。

void modify(int cur, int x, int y, int l, int r, int val) {
	if(l > y || r < x) return ; 
	else if(l >= x && r <= y) AddTag(cur, val, l, r);
	else {
		int mid = (l + r) / 2;
		pushdown(cur, l, r);
		modify(cur * 2, x, y, l, mid, val);
		modify(cur * 2 + 1, x, y, mid + 1, r, val);
		pushup(cur); //注意!!這裡要更新這個值,因為修改之後TA維護的值更改了,由於父親的值是取決於兒子的值,所以兒子改了之後,父親也得跟著改
	}
}

線段是的主要函式就將完了接下來就放完整程式碼嘍!(珍惜生命,不要CTJ)

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100015;
int n, m;
int sum[MAXN * 4]; //the core of the segment tree
int num[MAXN]; // Original array 
int tag[MAXN * 4]; // Remember!!!!! must"pushdown"
void modify(int cur, int x, int y, int l, int r, int val);
void pushdown(int x);
void pushup(int x);
int query(int cur, int x, int y, int l, int r);
void build(int cur, int l, int r);



signed main() {
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++) 
		scanf("%d", &num[i]);
	build(1, 1, n);
	for(int i = 1; i <= m; i ++) {
		int mod, x, y;
		scanf("%d%d%d", &mod, &x, &y);
		if(mod == 2) {
			printf("%d\n", query(1, x, y, 1, n));
		} else {
			int v;
			scanf("%d", &v);
			modify(1, x, y, 1, n, v);
		}
	}
	return 0;
}


void AddTag(int cur, int val, int l, int r) {
	tag[cur] += val;
	sum[cur] += (r - l + 1) * val;
}
void pushdown(int x, int l, int r) {
	if(tag[x] == 0) return ;
	int mid = (l + r) / 2;
	AddTag(x * 2, tag[x], l, mid);
	AddTag(x * 2 + 1, tag[x], mid + 1, r);
	tag[x] = 0;
}
void pushup(int x) {
	sum[x] = sum[x * 2] + sum[x * 2 + 1];
}
void modify(int cur, int x, int y, int l, int r, int val) {
	if(l > y || r < x) return ;   //!!!!!!!!!!!!!
	else if(l >= x && r <= y) AddTag(cur, val, l, r);
	else {
		int mid = (l + r) / 2;
		pushdown(cur, l, r);
		modify(cur * 2, x, y, l, mid, val);
		modify(cur * 2 + 1, x, y, mid + 1, r, val);
		pushup(cur);
	}
}
int query(int cur, int x, int y, int l, int r) {
	if(l > y || r < x) return 0;  //!!!!!!!!!!!!!
	else if(l >= x && r <= y) return sum[cur];
	else {
		int mid = (l + r) / 2;
		pushdown(cur, l, r);
		return query(cur * 2, x, y, l, mid) + query(cur * 2 + 1, x, y, mid + 1, r);
	}
}
void build(int cur, int l, int r) {
	if(l == r) {
		sum[cur] = num[l];
		return;
	}
	int mid = (l + r) / 2;
	build(cur * 2, l, mid);
	build(cur * 2 + 1, mid + 1, r);
	pushup(cur);
}

沒啦!