---
# System prepended metadata

title: 【C++】競程筆記（前綴和、差分 習題練習）
tags: [APCS, 程式設計, C++, 競程, 演算法, 前綴和, 差分, CP]

---

【C++】競程筆記（前綴和、差分 習題練習）
===

1. Zerojudge e346. 區間和練習：https://zerojudge.tw/ShowProblem?problemid=e346

:::info
內容：
給定一個長度為 $n$ 的整數序列 $A$ ，請回答 $q$ 筆詢問。每筆詢問會問其中一段連續區間的總和為何。

輸入說明：
* 輸入的第一行有一個整數 $n$ ( $1 \leq n \leq 200000$ )，代表 $A$ 序列的長度。
* 第二行有 $n$ 個整數以空白分隔，依序表示 $A$ 序列中的數字，其中數字的絕對值不會超過 $10^{9}$ 。
* 第三行有一個整數 $q$ ( $1 \leq q \leq 200000$ )，代表詢問的數量。
* 接下來有 $q$ 行，每行有兩個個數字 $l, r$ ，表示詢問為序列 $A$ 中第 $l$ 個數字到第 $r$ 個數字的總和。

輸出說明：
* 輸出 $q$ 行，每行有一個整數表示該次詢問的答案。
:::

![image](https://hackmd.io/_uploads/HkHgtrtzeg.png)

```cpp=
#include <bits/stdc++.h>

using namespace std;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    vector<long long> A(n, 0);
    for (int i = 0; i < n; ++i){
        cin >> A[i];
    }
    vector<long long> P(n + 1, 0);
    for (int i = 0; i < n; ++i){
        P[i + 1] = P [i] + A[i];
    }
    int q;
    cin >> q;
    for (int i = 0; i < q; ++i){
        int l, r;
        cin >> l >> r;
        cout << P[r] - P[l-1] << "\n";
    }
    return 0;
}
```

區間 $[l, r]$ 的部分，我試過題目給的是 1-based index，而非 0-based index，所以在寫的時候可將原本的：

```cpp=
// 0-based index
P[r + 1] - P[l];
```

改成：

```cpp=
// 1-based index
P[r] - P[l - 1];
```

或：

```cpp=
// 1-based index
l--, r--;
P[r + 1] - P[l];
```

然後記得輸出輸入優化，加上用 long long 資料型態，因為測資大到靠北。

2. ZeroJudge e367. 區間Xor：https://zerojudge.tw/ShowProblem?problemid=e367

:::info
內容：
因為c651太恐怖了,出一題簡單版的好了

有一個陣列 A. 且滿足:

1. $A[0] = 0$

2. `A[x]=A[x-1]^x` $(x \geq 1)$

所以 $A=[0,1,3,0,4,1,7,0,8......]$

而區間 Xor 定義如下：

給定兩個非負整數 $a, b$ $(a \leq b)$

則答案為 `A[a]^A[a+1]^A[a+2]^......^A[b-2]^A[b-1]^A[b]`

註：`^` 為 XOR 的位元運算子。

輸入說明：
* 每行兩個非負整數 $a,b$ $(0 < a \leq b \leq 10^{5})$

輸出說明：
* 輸出 $[a,b]$ 區間的每個數字進行 Xor 運算的結果
:::

![image](https://hackmd.io/_uploads/HkORbLtzll.png)

```cpp=
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 100000;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    vector<int> A(MAX_N + 1);
    vector<int> prefixXor(MAX_N + 1);
    
    A[0] = 0;
    prefixXor[0] = A[0];
    
    for (int i = 1; i <= MAX_N; ++i) {
        A[i] = A[i - 1] ^ i;
        prefixXor[i] = prefixXor[i - 1] ^ A[i];
    }

    int a, b;
    while (cin >> a >> b) {
        cout << (prefixXor[b] ^ prefixXor[a - 1]) << '\n';
    }

    return 0;
}
```

不難，其實就是把 `+` 號換成 `^` 號而已。

比較難的是 `prefixXor[b] ^ prefixXor[a - 1]` 這一條該怎麼求出來。

首先我們已經知道 `prefix[i] = A[0] ^ A[1] ^ A[2] ^ ... ^ A[i]`，表示從 `A[0]` 到 `A[i]` 的所有元素的 XOR 結果，則：

 $$prefix[b]=A[0]⊕A[1]⊕...⊕A[a−1]⊕A[a]⊕...⊕A[b]$$
 
 $$prefix[a−1]=A[0]⊕A[1]⊕...⊕A[a−1]$$

註：⊕ 為 XOR。

至於在 prefix[b] 的部分，為何會出現 A[a] 呢？不要忘記 [a, b] 區間，到 b 就有 a，只到 a 就不會有 b，如同 prefix[a−1] 那條式子一樣。

另外 XOR 有個很重要的性質（消除律）：

 $$x⊕x=0$$
 $$0⊕x=x$$
 
該性質可以消除相同的值：

 $$prefix[b]⊕prefix[a−1]=(A[0]⊕⋯⊕A[a−1]⊕A[a]⊕⋯⊕A[b])⊕(A[0]⊕⋯⊕A[a−1])$$

上面兩項中 A[0] ~ A[a-1] 因為出現兩次所以被消掉。

剩下 $A[a]⊕A[a+1]⊕⋯⊕A[b]$ 。

最後得到 $prefix[b]⊕prefix[a−1] = A[a]⊕A[a+1]⊕⋯⊕A[b]$ ，也就是我們要求的區間 Xor。
