1. 程式人生 > 其它 >(五) JavaSE基礎知識 - 變數、作用域、註釋

(五) JavaSE基礎知識 - 變數、作用域、註釋

技術標籤:洛谷線段樹資料結構

文章目錄

題目描述

如題,已知一個數列,你需要進行下面兩種操作:

將某區間每一個數加上k。
求出某區間每一個數的和。

輸入格式

第一行包含兩個整數 n,m,分別表示該數列數字的個數和操作的總個數。

第二行包含 n 個用空格分隔的整數,其中第 ii 個數字表示數列第 i 項的初始值。

接下來 m 行每行包含 3 或 4 個整數,表示一個操作,具體如下:

1 x y k:將區間 [ x , y ] [x, y] [x,y] 內每個數加上 k。
2 x y:輸出區間 [ x , y ] [x, y]

[x,y] 內每個數的和。

輸出格式

輸出包含若干行整數,即為所有操作 2 的結果。

輸入輸出樣例

輸入

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

輸出

11
8
20

說明

對於 30 % 30\% 30% 的資料: n ≤ 8 n \le 8 n8 m ≤ 10 m \le 10 m10
對於 70 % 70\% 70% 的資料: n ≤ 10 3 n \le {10}^3 n103 m ≤ 10 4 m \le {10}^4 m104
對於 100 % 100\% 100% 的資料: 1 ≤ n , m ≤ 10 5 1 \le n, m \le {10}^5

1n,m105

保證任意時刻數列中任意元素的和在 [ − 2 63 , 2 63 ) [-2^{63}, 2^{63}) [263,263) 內。

【樣例解釋】

在這裡插入圖片描述

AC的C++程式碼(結合註釋理解)

#include<iostream>
#define ll long long int
using namespace std;
const ll MAXN = 100005;

//a為原陣列,tree為線段樹,lazy為懶標記 
ll a[MAXN],tree[4*MAXN],lazy[4*MAXN],n,m;

//遞歸回溯
void push_up(ll root){
    tree[
root] = tree[root*2] + tree[root*2+1]; } //懶標記下傳 void push_down(ll root, ll l, ll r) { if (lazy[root]) { //左右子樹更新 ll len = r-l+1; tree[root*2] += (len-(len/2)) * lazy[root]; tree[root*2+1] += (len/2) * lazy[root]; //左右懶惰標記更新 lazy[root*2] += lazy[root]; lazy[root*2+1] +=lazy[root]; //取消該節點標記 lazy[root] = 0; } } // 對 [l,r] 區間建立線段樹,當前根的編號為 root void build(ll root, ll l, ll r) { if (l == r){ tree[root] = a[l]; return; } ll mid = (l + r) / 2; build(root * 2, l, mid); build(root * 2 + 1, mid + 1, r); // 遞迴對左右區間建樹 push_up(root); } void update(ll root,ll l,ll r,ll beg,ll end,ll k) { // 當前區間為修改區間的子集時直接修改當前節點的值,然後打標記,結束脩改 if (l>=beg && r<=end){ tree[root] += (r-l+1)*k; lazy[root] += k; return; } if(lazy[root]) push_down(root, l, r); ll mid=l+r>>1; if (beg <= mid) update(root*2,l,mid,beg,end,k); if (end > mid) update(root*2+1,mid+1,r,beg,end,k); push_up(root); } // [beg,end]為查詢區間,[l,r]為當前節點包含的區間,root為當前節點的編號 ll getsum(ll root,ll l,ll r,ll beg,ll end) { // 當前區間為詢問區間的子集時直接返回當前區間的和 if (beg <= l && r <= end) return tree[root]; // 如果當前節點的懶標記非空,則更新當前節點兩個子節點的值和懶標記值 if(lazy[root]) push_down(root, l, r); ll mid=l+r>>1,res=0; // 如果左兒子代表的區間 [l,m] 與詢問區間有交集,則遞迴查詢左兒子 if (beg <= mid) res += getsum(root*2,l,mid,beg,end); if (end > mid) res += getsum(root*2+1,mid+1,r,beg,end); return res; } int main(){ cin>>n>>m; //初始化原陣列 for(ll i = 1; i <= n; i++) cin >> a[i]; //建樹 build(1, 1, n); //進行加、求和操作 ll p, x, y, k; while(m--){ cin>>p; if(p == 1){ cin>>x>>y>>k; update(1, 1, n, x, y, k); } else if(p == 2){ cin>>x>>y; cout<<getsum(1, 1, n, x, y)<<endl; } } return 0; }