---
# System prepended metadata

title: '第二章 : 進階技巧 語法進階(前半)'

---

# 第二章 :進階技巧 語法進階(前半)

## 語法進階

### 巢狀迴圈

就是迴圈裡面套一個迴圈，APCS實作3級左右很愛考，其實觀念不難，自己寫一次體驗一下大概就能理解了。
```cpp=
for(int i=0;i<10;i++){
    for(int j=1;j<=10;j++) {
        cout<<(10*i+j)<<' ';
    }
    cout<<'\n';
}
```
結果大概會是這樣。
```txt=
1 2 3 4 5 6 7 8 9 10 
11 12 13 14 15 16 17 18 19 20 
21 22 23 24 25 26 27 28 29 30 
31 32 33 34 35 36 37 38 39 40 
41 42 43 44 45 46 47 48 49 50 
51 52 53 54 55 56 57 58 59 60 
61 62 63 64 65 66 67 68 69 70 
71 72 73 74 75 76 77 78 79 80 
81 82 83 84 85 86 87 88 89 90 
91 92 93 94 95 96 97 98 99 100 
```
執行流程是 
``i=0 => j=1 => cout => j++ => ...(j=11) => cout<<'\n' => i++ => j=1 => ...(直到i=10)``

### 巢狀迴圈中的break跟continue


### 二維陣列

通常搭配巢狀迴圈一起用，也是APCS第二題很喜歡考的內容，可以想像成 : 陣列是一串空間，每個空間存一個`int` ; 二維陣列則是一串陣列，二維陣列的每個空間存一個陣列，每個陣列又存一串空間。

```cpp=
int a[11][11];
for(int i=0;i<=10;i++){
    for(int j=0;j<=10;j++){
        a[i][j]=10*i+j;
    }
}
```
這時候陣列裡面就會是
```txt=
0 1 2 3 4 5 6 7 8 9 10 
10 11 12 13 14 15 16 17 18 19 20 
20 21 22 23 24 25 26 27 28 29 30 
30 31 32 33 34 35 36 37 38 39 40 
40 41 42 43 44 45 46 47 48 49 50 
50 51 52 53 54 55 56 57 58 59 60 
60 61 62 63 64 65 66 67 68 69 70 
70 71 72 73 74 75 76 77 78 79 80 
80 81 82 83 84 85 86 87 88 89 90 
90 91 92 93 94 95 96 97 98 99 100 
100 101 102 103 104 105 106 107 108 109 110 
```
可以試著輸出看看
```cpp=
for(int i=0;i<=10;i++){
    for(int j=0;j<=10;j++){
        cout<<a[i][j]<<' ';
    }
    cout<<'\n';
}
```

***注意*** : 小心不要超出陣列範圍，不然會RE，是APCS第二題很常出現的錯誤

> 一些練習題
> [APCS模擬賽 地震](https://apcs-simulation.com/problem/apcs0502)
> [地震題解](https://hackmd.io/@apcs-simulation-/rkgCnXJ40)
> [APCS模擬賽 天旋地轉](https://apcs-simulation.com/problem/apcs0802)
> [天旋地轉題解](https://hackmd.io/@apcs-simulation-/ByKdMix30)

### define

C++可以用`#define`某些關鍵字在編譯時換成其他字，聽起來有點抽象，舉個例子。
```cpp=
#define a b
```
這樣之後的所有a在程式裡都會被電腦當成b。
```cpp=
#define a b
cout<<a<<'\n'; //編譯器會當成 cout<<b<<'\n';
```
幾個常用的用法
1. `#define int long long`

有一些題目可能會有很多變數會超出`long long`範圍，寫的時候又要打很多`long long`很麻煩，加上這句後所有`int`都會被當成`long long`。
```cpp=
#define int long long
int a[10]; //他其實是long long 陣列
```
不過有幾點要注意，`int main()`的`int`也會被換成`long long`，編譯器就會報錯，所以要改成下面的程式。
```cpp=
signed main(){
    //...
}
```
因為int的其實是`signed int`，打`signed`的話編譯器也會把他當`int`。
另外，`long long`的空間是`int`的兩倍，在有些卡比較緊的題目中，所有變數都開成l`ong long`可能會`MLE/TLE`，不過筆者認為利大於弊，以筆者的經驗來說，忘記開`long long`而overflow的次數比開`long long`被卡時間空間的次數多很多很多。

2. `#define f/s first/second`

這通常是在會用到`std::pair`的題目會加的，因為`first(second)`太長了qwq。

3. `#define ALL(x) x.begin(),x.end()`

有些人喜歡在使用`std::vector`或其他$STL$時使用。

### struct

這東西有點像自己定義變數型態，可以把很多個變數綁在一起
![image alt](https://s3.amazonaws.com/media-p.slid.es/uploads/2780100/images/11533542/pasted-from-clipboard.png)
像是這樣，可以放int可以放float可以放字串可以放陣列甚至可以放函式，宣告方法如下
```cpp=
struct a{
    int l,r;
    char op;
    string ans;
    double point;
    bool comp(int a,int b){
        //...
    }
};
```
這樣就宣告了一個struct，**但他還只是一個變數型態，不是一個變數**，想要有一個真正的struct只要跟一般變數宣告方法一樣
```cpp=
a b;
```
這樣b就是一個包含很多個變數的變數了，如果要使用b其中的變數，要用.
```cpp=
b.ans="hello";
cout<<b.ans<<'\n';
```
如此，其他int、char、double同理，函數的使用方法一樣
```cpp=
b.comp(x,y);
```
struct裡的變數在宣告時和一般變數相同，若在全域宣告會是0，若在函式內宣告則會是亂數，想要控制宣告時的初始值，可使用建構函式
```cpp=
struct a{
    int l,r;
    a(int x,int y):l(x),r(y){}
};
```
函式前面不用宣告回傳值，函式名字直接使用struct的名字，加上一個冒號接著可以初始化裡面的變數，用法 ``變數名稱(值)``，後面的大括號內可寫入程式，在宣告時會被執行
這樣宣告時傳入$x,y$就可以把變數中的$l,r$初始化為$x,y$
```cpp=
a b(1,2);
cout<<b.l<<' '<<b.r<<'\n';
```
另外，struct的運算子如果沒有特別宣告都是未定義的，像是``+``、``-``、``*``、``/``、``=``、``>``、``<``等等，要使用的話要重載運算子
```cpp=
struct a{
    int l,r;
    bool operator < (a o) const{
        return l<o.l;
    }
};
```
像是這樣，前面先宣告回傳類型，接著``operator``然後你想重載的運算子，後面就和一般函式一樣。

### 指標

這東東在開發中有非常非常多應用，但以APCS(或競賽程式)為目標的話，只需要了解指標的概念即可，不太需要理解過於深奧的指標用法。

了解指標之前，先從變數開始 ; 變數，用來儲存資料的資料結構，想當然他必須占用電腦上的記憶體，而由於記憶體空間很大，電腦為了更好的管理空間，每個記憶體空間都有被分配一個編號，類似於住址，以下稱其為**記憶體位址**。

C/C++中，有兩種運算子可以控制記憶體位址，以下為範例 : 
```cpp=
int a=1;  //一般變數

cout<<(&a)<<'\n';  // & 運算子可以得到 a 變數的記憶體位址

cout<<*(&a)<<'\n';  // * 運算子則是取得記憶體位址儲存的資料
```
``&``後接==變數==，功能為取得該變數的記憶體位址 ;
``*``後接==記憶體位址==，功能為取得該記憶體位址的變數。

當然，透過記憶體位址我們也可以修改該位址所儲存的資料
```cpp=
int a=1;
cout<<a<<'\n';  // 會輸出 1
*(&a)=2;
cout<<a<<'\n';  // 會輸出 2
```

在C/C++中，指標就是一種儲存記憶體位址的變數，因此，指標可以很直接地對硬體進行控制，功能強大的同時，也很容易在使用不慎時導致程式執行時發生錯誤。

以下展示指標的宣告方法 : 
```cpp=
int a=1;
int *p=&a;  // 透過 * 可以宣告指標變數，儲存 a 變數的記憶體位址

cout<<p<<'\n';  //會輸出a的記憶體位址
cout<<*p<<'\n';  //會輸出a的值 (即 1)

*p+=3;
cout<<*p<<'\n';  //一樣可以修改a變數 (輸出 4) 
```
想要儲存哪一種類型的變數，就得宣告該類型的指標，透過``*``代表他是指標變數，賦值時賦予記憶體位址。

指標變數也是一種變數，因此，他也需要記憶體空間，當然也有屬於他的記憶體位址
```cpp=
int a;
int *p=&a; //指向一般變數的指標
int **pp=&p;  //指向指標的指標
int ***ppp=&pp;  //指向指向指標的指標的指標
```

- 指標的應用

陣列，是一串記憶體位址連續的變數，而C/C++中，陣列其實是儲存陣列第一個變數的記憶體位址的指標，如此，就不需要記住每個變數的實際記憶體位址，剩下的變數透過加減即可得到
```cpp=
int a[10]={0,1,2,3,4,5,6,7,8,9};
cout<<a<<' '<<&a[0]<<'\n';  //是相同的東西

cout<<*(a+3)<<' '<<a[3]<<'\n';  //也是相同的東西
```
``[]``其實就是加法後取值，因此，陣列的index不一定要為正
```cpp=
int a[10]={0,1,2,3,4,5,6,7,8,9};
int *b=a+5;
cout<<b[-3]<<' '<<a[2]<<'\n';  //還是相同的東西
```

在第三章基礎資結會說到**STL**，而STL有一個特殊的**疊代器**，剛接觸時可能不太能理解他是甚麼，畢竟他的名字聽起來也非常高大上，但他其實就是C/C++開發者特別設計的指標，以後看到時可以就把他當成記憶體位址。

---

### 題目
- 多維陣列
[ABC364B](https://atcoder.jp/contests/abc364/tasks/abc364_b)