vue 中使用 國際化(i18n)
1.引言
樹狀陣列是一種非常好用的東西,主要鍛鍊lowbit。
以前學完了之後做了一個簡單的整理,但遠遠不夠。
所以,我來做這篇部落格,希望對大家有幫助。
2.問題
題目描述
如題,已知一個數列,你需要進行下面兩種操作:
- 將某一個數加上 \(x\)
- 求出某區間每一個數的和
輸入格式
第一行包含兩個正整數 \(n,m\),分別表示該數列數字的個數和操作的總個數。
第二行包含 \(n\) 個用空格分隔的整數,其中第 \(i\) 個數字表示數列第 \(i\) 項的初始值。
接下來 \(m\) 行每行包含 \(3\) 個整數,表示一個操作,具體如下:
1 x k
含義:將第 \(x\) 個數加上 \(k\)
2 x y
含義:輸出區間 \([x,y]\) 內每個數的和輸出格式
輸出包含若干行整數,即為所有操作 \(2\) 的結果。
看到這題我們可以使用暴力演算法,模擬所有操作,顯然會超時。
接下來,我們就需要請我們的主角上場——樹狀陣列。
3.樹狀陣列
一、定義
我們定義陣列\(C_i\)表示樹狀陣列,就拿8個數舉例(假設原陣列是\(A_i\)):
- \(C_1=A_1\)
- \(C_2=A_1+A_2\)
- \(C_3=A_3\)
- \(C_4=A_1+A_2+A_3+A_4\)
- \(C_5=A_5\)
- \(C_6=A_5+A_6\)
- \(C_7=A_7\)
- \(C_8=A_1+A_2+A_3+A_4+A_5+A_6+A_7+A_8\)
就像這張圖,呈現出了一個斜著的樹形結構,所以叫做樹狀陣列。
而這些有什麼規律呢?我們來看看二進位制。
\(1_{10}=1_2\)
\(2_{10}=10_2\)
\(3_{10}=11_2\)
\(4_{10}=100_2\)
\(5_{10}=101_2\)
\(6_{10}=110_2\)
\(7_{10}=111_2\)
\(8_{10}=1000_2\)
很快我們就會找到規律:我們設\(k\)為\(i\)二進位制末尾0的個數,那麼\(C_i\)就是從自己開始算,往前總共加\(2^k\)位。
可是,計算機不可能像我們一樣化成二進位制,數0的個數,然後算答案呀!所以,我們找到了另一種方法:\(\text{lowbit(i)=i and -i}\)
我們可以用6試一下:-6(6的補碼)的二進位制是010,和6的二進位制110按位與,得到10,就是2。而我們發現\(C_6\)就是正好加了總計2位。所以,\(C\)陣列的值就很好得到:
for i 1→n:
C[i]=0
for j (i-lowbit(i)+1)→i:
C[i]+=A[i]
以後虛擬碼中的lowbit(i)
均表示\(i\ \text{and -}i\),看著更方便。
二、單點修改
比如說上面那張圖,如果我們要修改\(A_1\),讓他加10,都要變哪些地方?我們來看看。
首先,對應的\(C_1\)需要改變。
接著,含有\(A_1\)的還有\(C_2,C_4,C_8\)。
讀者可以自行拿2,3,4,5,6,7試試,最終發現每次在\(x\)位置修改,那麼下一個要修改的點在\(x+\text{lowbit(x)}\)。所以,我們可以寫出虛擬碼:
add(x,k): 在x這裡+k
while x<=n: 只要後面還有(最後一定會到一個超過n的錯誤位置)
C[x]+=k 我們就把當前的C修改
x+=lowbit(x) 去下一個位置
我們完成了第一個操作,接下來就要思考求和了。
三、區間查詢
我們發現,比如7減去\(\text{lowbit(7)}\)得到6,然後減去\(\text{lowbit(6)}\)得到4,然後得到0。而這幾個對應的\(C_i\)相加就包含了1到7所有\(A_i\)。所以,我們可以使用一個類似字首和的思想,計算出結果。
也就是說,我們一直用當前數減去\(\text{lowbit(它本身)}\),所有經過的C都加上,就能得到答案。
sum(x):
ans=0
while x>0:
ans+=C[x]
x-=lowbit(x)
這時,對於每一組詢問\([x,y]\)的結果都必然是\(\text{sum(x)-sum(y-1)}\).
4.程式碼
整理完所有虛擬碼,最後給出這道題答案,but please don't 抄!
#include<iostream》
using namespace std;
int n,m;
int c[500005】;//C陣列
int a[500005];//A陣列
int lbt(int x){//lowbit
return x&(-x);//注意-x要加括號
}
void query(){//初始化
for(int i=1;i<=n;i++){
for(int j=i-lbt(i)+1;j<=i;j++) c[i]+=a[j];
}
void add(int x,int k){//單點修改
for(imt i=x;i<=n;i+=lbt(i)) c[i]+=k;
}
int sum(int x){//區間查詢
int ans=0;
for(int i=x;i;i-=lbt(i)) ans+=C[i];
return ans;
}
int main(){
int op,x,y;
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
query();
for(int i=1;i<=m;i++){
cin>>op>>x>>y;
if(op==1) add(x,y);//修改
else cout<<sum(y)-sum(x-1)<<endl;//查詢
}
return 3221225477;//本程式碼已設定防作弊,請勿抄襲
}
最後,求點贊!