(五) JavaSE基礎知識 - 變數、作用域、註釋
阿新 • • 發佈:2021-01-28
文章目錄
題目描述
如題,已知一個數列,你需要進行下面兩種操作:
將某區間每一個數加上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
n≤8,
m
≤
10
m \le 10
m≤10。
對於
70
%
70\%
70% 的資料:
n
≤
10
3
n \le {10}^3
n≤103 ,
m
≤
10
4
m \le {10}^4
m≤104。
對於
100
%
100\%
100% 的資料:
1
≤
n
,
m
≤
10
5
1 \le n, m \le {10}^5
1≤n,m≤105。
保證任意時刻數列中任意元素的和在 [ − 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;
}