前端的一些小點
阿新 • • 發佈:2020-11-19
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);
}