# 秘笈
考前看一下,放輕鬆,等等一定都AC!
## 宣告
```cpp=
vector<vector<int>> v1(n,vector<int>(m,0)); // 二維vector宣告+初始化
// 其它型態:bool, pair<int,int>, char, string, double, long long
// 可用 typedef pair<pair<int,int>,int> side 簡化宣告,接下來使用side即可宣告
```
## 輸入
* 正常輸入 n 筆
```cpp=
int n;
cin >> n;
for(int i=0;i<n;i++){
//cin >>
}
```
* 輸入直到0為止
```cpp=
int n;
while(cin >> n){
if(n==0) break;
// 正常操作
}
```
* 整行讀取,以逗號(或字元)分隔
```cpp=
string s;
getline(cin,s);
stringstream ss(s);
int n;
char c;
while(cin >> n >> c){
// 正常操作
}
// 注意,若 n,c 數量不等,可以在字串尾端增加一個無用字元
// s+=','
// 這樣就可以順利讀取所有 n
// 舉例:1,2,3,4,5,照上方讀取只會得到1 2 3 4,5 因為沒有逗號所以while不成立
// 補上尾端逗號以後就變成 1,2,3,4,5,,就能順利讀取了
```
```cpp=
// 也可以直接做,對於逗號的部分,如果是其他字元有特殊用途(例如數學函式計算)可另做判斷
string s;
cin >> s;
for(int i=0;i<s.size();i++){
if(s[i]==',') continue;
// 正常操作
}
// isdigit()可以視情況使用,自帶判斷該字元是否為整數的功能,是 bool
```
## 型別轉換
* 字元轉整數
```cpp=
char c;
cin >> c;
int n=c-'0';
```
* 整數轉字元
```cpp=
int n;
cin >> n;
char c=n+'0';
```
* 字串轉整數
```cpp=
string s;
cin >> s;
int i = stoi(s);
```
* 整數轉字串
```cpp=
int x;
cin >> x;
string s = to_string(x);
```
## 常用STL
* 排序
```cpp=
// O(nlogn)
vector<pair<int,int>> v;
sort(v.begin(),v.end());
// 注意,若未指定排序,會依照第一個元素開始升冪排序,也就是 .first
// 自訂排序可以宣告副函式cmp
/*
bool cmp(int a,int b){
return a>b;
}
sort(v.begin(),v.end(),cmp);
*/
```
* 序列
```cpp=
// O(1)插入 & 取值
queue<int> q;
// 可用函式:q.push(),q.pop(),q.front(),q.empty(),q.size()
deque<int> dq;
// 可用函式:dq.push_back(),dq.push_front(),dq.pop_back(),dq.pop_front(),dq.front(),dq.back(),dq.empty(),dq.size()
```
* 最大最小堆
```cpp=
// 插入O(log n),取值O(1)
priority_queue<int> pq1; // 最大堆
// 可用函式:pq.push(),pq.top(),pq.pop(),pq.empty(),pq.size()
pruoruty_queue<int,vector<int>,greater<int>> pq2; // 最小堆
// 也是可以把存進去的值加上負號,直接拿最大堆當最小堆用
```
* 堆疊
```cpp=
// O(1)插入 & 取值
stack<int> st;
// 可用函式:st.push(),q.pop(),st.top(),st.empty(),st.size()
```
* set
```cpp=
// O(log n)插入 & 取值
set<int> s;
// 可用函式:s.insert(),s.count(),s.find(),s.erase(),s.clear(),s.empty(),s.size()
/* 補充find,count用法:
if(s.find(n)!=s.end()){
// 判斷有無出現
}
//也可以寫成這樣
if(count(n)){
// 如果是set會去重,只出現0或1
}
*/
unordered_set<int> us; // 無序
multiset<int> ms; // 可重複
```
* map
```cpp=
// O(log n) 插入 & 取值
map<string, int> mp;
// 可用函式:mp[],mp.erase(),mp.find(),mp.count(),mp.clear(),mp.empty(),mp.size()
/* 常見寫法
mp[key] = value;
my[key]++; // 計數的寫法
if(mp.find("key") != mp.end()){
cout << mp["key"];
}
*/
unordered_map<string, int> ump; // 無序
multimap<string, int> mmp; // 可重複
```
## 圖論
* 密集圖
```cpp=
// 密集圖存點
int n,m; // 地圖大小n*m
cin >> n >> m;
vector<vector<int>> v(n,vector<int>(m));
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cin >> v[i][j];
}
}
```
```cpp=
// 密集圖BFS
int dx[4]={0,0,1,-1}; // 可以改8方向
int dy[4]={1,-1,0,0};
vector<vector<int>> v(n,vector<int>(m)); // 圖
vector<vector<bool>> vis(n,vector<bool>(m,false)); // 拜訪狀態
vector<vector<int>> dis(n,vector<int>(m)); // 最短距離(或者其他要記錄的東西)
queue<pair<int,int>> q;
q.push({0,0}); // 假設 (0,0) 為起點
vis[0][0]=true;
dis[0][0]=0;
while(!q.empty()){
auto now=q.front();
q.pop();
int nowx=now.first;
int nowy=now.second;
for(int i=0;i<4;i++){
int nx=nowx+dx[i],ny=nowy+dy[i];
if(nx>=0 and nx<n and ny>=0 and ny<m){ // 越界檢查
if(!vis[nx][ny] and v[nx][ny]!=-1){ // 假設-1是牆無法通行
q.push({nx,ny});
vis[nx][ny]=true;
dis[nx][ny]=dis[nowx][nowy]+1;
}
}
}
}
cout << dis[n-1][m-1]; // 假設 (n-1,m-1) 為終點
```
```cpp=
// 密集圖DFS
int dx[4] = {0, 0, 1, -1}; // 可以改8方向
int dy[4] = {1, -1, 0, 0};
vector<vector<int>> v(n, vector<int>(m)); // 圖
vector<vector<bool>> vis(n, vector<bool>(m, false)); // 拜訪狀態
vector<vector<int>> dis(n, vector<int>(m)); // 最短距離(或其他要記錄的東西)
void dfs(int nowx, int nowy, int d) {
vis[nowx][nowy] = true;
dis[nowx][nowy] = d;
for (int i = 0; i < 4; i++) {
int nx = nowx + dx[i], ny = nowy + dy[i];
if (nx >= 0 and nx < n and ny >= 0 and ny < m) { // 越界檢查
if (!vis[nx][ny] and v[nx][ny] != -1) { // 假設 -1 是牆無法通行
dfs(nx, ny, d + 1);
}
}
}
}
dfs(0, 0, 0); // 假設 (0,0) 為起點
cout << dis[n-1][m-1]; // 假設 (n-1,m-1) 為終點
```
* 疏散圖
```cpp=
// 疏散圖存邊
int n,m; // n個點,m條邊
cin >> n >> m;
vector<vector<pair<int,int>>> v(n):
for(int i=0;i<m;i++){
int x,y,z; // 連接兩點,權重
cin >> x >> y >> z;
v[x].push_back({y,z});
v[y].push_back({x,z}); // 無向圖才需要反向存
}
```
```cpp=
// 疏散圖BFS
vector<vector<pair<int,int>>> v(n); // 圖(pair<鄰點,權重>)
vector<bool> vis(n, false); // 拜訪狀態
vector<int> dis(n, 1e9); // 最短距離(或者其他要記錄的東西)
queue<int> q;
q.push(0); // 假設 0 為起點
vis[0] = true;
dis[0] = 0;
while (!q.empty()) {
int now = q.front();
q.pop();
for (auto it : v[now]) { // it.first = 鄰點, it.second = 權重
int nxt = it.first;
if (!vis[nxt]) {
q.push(nxt);
vis[nxt] = true;
dis[nxt] = dis[now] + it.second; // 無權圖距離 +1
}
}
}
cout << dis[n-1]; // 假設 n-1 為終點
```
```cpp=
// 疏散圖DFS
vector<vector<pair<int,int>>> v(n); // 圖 (pair<鄰點,權重>)
vector<bool> vis(n, false); // 拜訪狀態
vector<int> dis(n, 1e9); // 最短距離(或者其他要記錄的東西)
void dfs(int now, int d) {
vis[now] = true;
dis[now] = min(dis[now], d);
for (auto it : v[now]) { // it.first = 鄰點, it.second = 權重
int nxt = it.first;
int w = it.second; // 權重,可為 1 代表無權圖
if (!vis[nxt]) {
dfs(nxt, d + w);
}
}
}
dfs(0, 0); // 假設 0 為起點
cout << dis[n-1]; // 假設 n-1 為終點
```
## 進階圖論
```cpp=
// 拓樸排序(Kahn算法)
vector<vector<int>> g(n); // 圖
vector<int> indeg(n, 0); // 入度
queue<int> q;
vector<int> topo; // 結果序列
// 建圖後:g[u].push_back(v); indeg[v]++;
// 初始化:將所有入度為0的點入隊
for (int i = 0; i < n; i++)
if (indeg[i] == 0) q.push(i);
// BFS 依序處理
while (!q.empty()) {
int u = q.front(); q.pop();
topo.push_back(u);
for (int v : g[u]) {
indeg[v]--;
if (indeg[v] == 0) q.push(v);
}
}
// 若 topo.size() < n → 圖中有環
for (int x : topo) cout << x << " ";
```
尤拉路徑:恰有 0 或 2 個奇度數頂點
尤拉迴路:所有點度數皆為偶數
```cpp=
// 無向圖 尤拉路徑 (Hierholzer演算法)
#include <bits/stdc++.h>
using namespace std;
int n, m;
vector<vector<pair<int,int>>> g;
vector<bool> used;
vector<int> path;
void dfs(int u){
for(auto [v,id] : g[u]){
if(used[id]) continue;
used[id] = true;
dfs(v);
}
path.push_back(u);
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
g.assign(n, {});
used.assign(m, false);
for(int i=0;i<m;i++){
int u,v;
cin >> u >> v;
g[u].push_back({v,i});
g[v].push_back({u,i});
}
// 找起點(若有兩個奇度點,從其中一個開始)
int start = 0;
int odd = 0;
for(int i=0;i<n;i++){
if(g[i].size() % 2 == 1){
odd++;
start = i;
}
}
if(odd != 0 && odd != 2){
cout << "No Euler Path\n";
return 0;
}
dfs(start);
reverse(path.begin(), path.end());
if((int)path.size() != m+1){
cout << "Graph not connected or edges left\n";
return 0;
}
cout << "Euler Path:\n";
for(int v : path) cout << v << " ";
cout << "\n";
}
```
```cpp=
// 簡單環(無向圖)
#include <bits/stdc++.h>
using namespace std;
int n, m;
vector<vector<int>> g;
vector<int> path;
vector<bool> vis;
set<vector<int>> cycles; // 用 set 避免重複環
void dfs(int u, int parent) {
vis[u] = true;
path.push_back(u);
for (int v : g[u]) {
if (v == parent) continue; // 不走回父節點
auto it = find(path.begin(), path.end(), v);
if (it != path.end()) {
// 找到環
vector<int> cycle(it, path.end());
// 讓環的起點最小,避免重複
rotate(cycle.begin(), min_element(cycle.begin(), cycle.end()), cycle.end());
cycles.insert(cycle);
} else if (!vis[v]) {
dfs(v, u);
}
}
path.pop_back();
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
g.assign(n, {});
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
for (int i = 0; i < n; i++) {
vis.assign(n, false);
path.clear();
dfs(i, -1);
}
cout << "Simple cycles found:\n";
for (auto &cyc : cycles) {
for (int x : cyc) cout << x << " ";
cout << cyc[0] << "\n"; // 回到起點
}
}
```
```cpp=
// Tarjan 找橋演算法(無向圖)
#include <bits/stdc++.h>
using namespace std;
int n, m;
vector<vector<pair<int,int>>> g;
vector<int> tin, low;
vector<bool> vis;
int timer = 0;
void dfs(int u, int p_edge) {
vis[u] = true;
tin[u] = low[u] = ++timer;
for (auto [v, id] : g[u]) {
if (id == p_edge) continue; // 不走回來的那條邊
if (vis[v]) {
// 反向邊(往祖先連)
low[u] = min(low[u], tin[v]);
} else {
dfs(v, id);
low[u] = min(low[u], low[v]);
if (low[v] > tin[u]) {
cout << "Bridge found: " << u << " - " << v << "\n";
}
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
g.assign(n, {});
tin.assign(n, -1);
low.assign(n, -1);
vis.assign(n, false);
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
g[a].push_back({b, i});
g[b].push_back({a, i});
}
for (int i = 0; i < n; i++)
if (!vis[i])
dfs(i, -1);
}
```
```cpp=
// Tarjan SCC(有向圖)
#include <bits/stdc++.h>
using namespace std;
int n, m, timer = 0, scc_count = 0;
vector<vector<int>> g;
vector<int> tin, low, scc_id;
vector<bool> in_stack;
stack<int> stk;
void dfs(int u) {
tin[u] = low[u] = ++timer;
stk.push(u);
in_stack[u] = true;
for (int v : g[u]) {
if (tin[v] == 0) {
dfs(v);
low[u] = min(low[u], low[v]);
} else if (in_stack[v]) {
// 回邊 → 更新 low[u]
low[u] = min(low[u], tin[v]);
}
}
// 若是 SCC 根
if (low[u] == tin[u]) {
cout << "SCC #" << ++scc_count << ": ";
while (1) {
int v = stk.top(); stk.pop();
in_stack[v] = false;
cout << v << " ";
scc_id[v] = scc_count;
if (v == u) break;
}
cout << "\n";
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> m;
g.assign(n + 1, {});
tin.assign(n + 1, 0);
low.assign(n + 1, 0);
scc_id.assign(n + 1, 0);
in_stack.assign(n + 1, false);
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b); // 有向邊
}
for (int i = 1; i <= n; i++)
if (tin[i] == 0)
dfs(i);
}
```
## 最短路徑演算法
```cpp=
// Dijkstra
#include <bits/stdc++.h>
using namespace std;
struct Edge { int to; long long w; };
const long long INF = 1e18;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, s;
cin >> n >> m >> s;
vector<vector<Edge>> g(n + 1);
for (int i = 0; i < m; i++) {
int u, v; long long w;
cin >> u >> v >> w;
g[u].push_back({v, w});
// 若是無向圖再加這行:
// g[v].push_back({u, w});
}
vector<long long> dist(n + 1, INF);
priority_queue<pair<long long,int>, vector<pair<long long,int>>, greater<>> pq;
dist[s] = 0;
pq.push({0, s});
while (!pq.empty()) {
auto [d, u] = pq.top(); pq.pop();
if (d != dist[u]) continue;
for (auto [v, w] : g[u]) {
if (dist[v] > d + w) {
dist[v] = d + w;
pq.push({dist[v], v});
}
}
}
for (int i = 1; i <= n; i++)
cout << (dist[i] == INF ? -1 : dist[i]) << " ";
}
```
```cpp=
// Bellman-Ford(允許負邊)
#include <bits/stdc++.h>
using namespace std;
struct Edge { int u, v; long long w; };
const long long INF = 1e18;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m, s;
cin >> n >> m >> s;
vector<Edge> edges(m);
for (int i = 0; i < m; i++) cin >> edges[i].u >> edges[i].v >> edges[i].w;
vector<long long> dist(n + 1, INF);
dist[s] = 0;
for (int i = 1; i < n; i++)
for (auto [u, v, w] : edges)
if (dist[u] != INF && dist[v] > dist[u] + w)
dist[v] = dist[u] + w;
// 負環檢查
bool neg = false;
for (auto [u, v, w] : edges)
if (dist[u] != INF && dist[v] > dist[u] + w)
neg = true;
if (neg) cout << "Negative cycle detected\n";
else
for (int i = 1; i <= n; i++)
cout << (dist[i] == INF ? -1 : dist[i]) << " ";
}
```
```cpp=
// Floyd-Warshall
// O(n^3)
#include <bits/stdc++.h>
using namespace std;
const long long INF = 1e15;
long long d[505][505];
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
d[i][j] = (i == j ? 0 : INF);
for (int i = 0; i < m; i++) {
int u, v; long long w;
cin >> u >> v >> w;
d[u][v] = min(d[u][v], w);
// 若是無向圖再加:
// d[v][u] = min(d[v][u], w);
}
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++)
cout << (d[i][j] == INF ? -1 : d[i][j]) << " ";
cout << '\n';
}
}
```
## 樹論
```cpp=
// 樹上BFS
vector<vector<int>> tree(n);
vector<int> dis(n, -1);
queue<int> q;
q.push(0);
dis[0] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : tree[u]) {
if (dis[v] == -1) {
dis[v] = dis[u] + 1;
q.push(v);
}
}
}
```
```cpp=
// 樹上DFS
vector<vector<int>> tree(n);
vector<bool> vis(n, false);
void dfs(int u, int p) { // p 為父節點
vis[u] = true;
for (int v : tree[u]) {
if (v == p) continue;
dfs(v, u);
}
}
dfs(0, -1);
```
從任意點 A 出發,找到距離最遠的點 B
從 B 出發,再找最遠的點 C
B–C 的距離即為直徑
```cpp=
// 樹直徑 兩次 BFS
vector<vector<int>> tree(n);
vector<int> dis(n);
auto bfs = [&](int s) {
fill(dis.begin(), dis.end(), -1);
queue<int> q;
q.push(s);
dis[s] = 0;
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v : tree[u]) {
if (dis[v] == -1) {
dis[v] = dis[u] + 1;
q.push(v);
}
}
}
};
bfs(0);
int A = max_element(dis.begin(), dis.end()) - dis.begin();
bfs(A);
int diameter = *max_element(dis.begin(), dis.end());
cout << diameter;
```
```cpp=
// LCA 二分跳躍
const int LOG = 20;
vector<vector<int>> tree(n);
vector<vector<int>> up(n, vector<int>(LOG)); // up[u][k] = u 的第 2^k 層祖先
vector<int> depth(n);
void dfs(int u, int p) {
up[u][0] = p;
for (int k = 1; k < LOG; k++)
up[u][k] = up[up[u][k-1]][k-1];
for (int v : tree[u]) {
if (v == p) continue;
depth[v] = depth[u] + 1;
dfs(v, u);
}
}
int lca(int a, int b) {
if (depth[a] < depth[b]) swap(a, b);
// 拉高 a
int diff = depth[a] - depth[b];
for (int k = 0; k < LOG; k++)
if (diff >> k & 1)
a = up[a][k];
if (a == b) return a;
// 同步往上跳
for (int k = LOG-1; k >= 0; k--) {
if (up[a][k] != up[b][k]) {
a = up[a][k];
b = up[b][k];
}
}
return up[a][0];
}
```
## 數論
```cpp=
long long gcd(long long a, long long b) {
while (b) {
long long t = a % b;
a = b;
b = t;
}
return a >= 0 ? a : -a;
}
long long lcm(long long a, long long b) {
if (a == 0 || b == 0) return 0;
return a / mygcd(a, b) * b;
}
```
```cpp=
bool isPrime(long long n) {
if (n < 2) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (long long i = 5; i * i <= n; i += 6) {
if (n % i == 0 || n % (i + 2) == 0)
return false;
}
return true;
}
```
```cpp=
// 解方程式交點(兩點式)
#include <bits/stdc++.h>
using namespace std;
const double EPS = 1e-9;
struct Point {
double x, y;
};
// 將兩點式 (x1,y1),(x2,y2) 轉為一般式 Ax + By = C
void twoPointToGeneral(double x1, double y1, double x2, double y2,
double &A, double &B, double &C) {
A = y2 - y1; // Δy
B = x1 - x2; // -Δx
C = A * x1 + B * y1; // 套入一點求 C
}
// 求兩條線的交點
pair<bool, Point> line_intersection(Point p1, Point p2, Point p3, Point p4) {
double A1, B1, C1, A2, B2, C2;
twoPointToGeneral(p1.x, p1.y, p2.x, p2.y, A1, B1, C1);
twoPointToGeneral(p3.x, p3.y, p4.x, p4.y, A2, B2, C2);
double det = A1 * B2 - A2 * B1;
if (fabs(det) < EPS) return {false, {0, 0}}; // 平行或重合
double x = (C1 * B2 - C2 * B1) / det;
double y = (A1 * C2 - A2 * C1) / det;
return {true, {x, y}};
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
Point p1, p2, p3, p4;
cin >> p1.x >> p1.y >> p2.x >> p2.y;
cin >> p3.x >> p3.y >> p4.x >> p4.y;
auto [ok, P] = line_intersection(p1, p2, p3, p4);
if (ok)
cout << fixed << setprecision(6) << P.x << " " << P.y << "\n";
else
cout << "no intersection\n";
}
```
```cpp=
// 解方程式交點(直線方程式)
#include <bits/stdc++.h>
using namespace std;
const double EPS = 1e-9;
pair<bool, pair<double,double>> line_intersection(
long long A, long long B, long long C,
long long D, long long E, long long F) {
double det = A * E - B * D;
if (fabs(det) < EPS) return {false, {0, 0}}; // 平行或重合
double x = (C * E - B * F) / det;
double y = (A * F - C * D) / det;
return {true, {x, y}};
}
int main() {
long long A,B,C,D,E,F;
cin >> A >> B >> C >> D >> E >> F;
auto [ok, p] = line_intersection(A,B,C,D,E,F);
if (ok) cout << fixed << setprecision(6) << p.first << " " << p.second;
else cout << "no intersection";
}
```
## DP
```cpp=
// 背包問題
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, W;
cin >> n >> W;
vector<int> w(n+1), v(n+1);
for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];
vector<vector<int>> dp(n+1, vector<int>(W+1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= W; j++) {
dp[i][j] = dp[i-1][j];
if (j >= w[i])
dp[i][j] = max(dp[i][j], dp[i-1][j - w[i]] + v[i]);
}
}
cout << dp[n][W];
}
```
```cpp=
// LCS
#include <bits/stdc++.h>
using namespace std;
int main() {
string A, B;
cin >> A >> B;
int n = A.size(), m = B.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
// 填 DP 表
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (A[i - 1] == B[j - 1])
dp[i][j] = dp[i - 1][j - 1] + 1;
else
dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
// 反向追溯 LCS 字串
string lcs = "";
int i = n, j = m;
while (i > 0 && j > 0) {
if (A[i - 1] == B[j - 1]) {
lcs += A[i - 1];
i--; j--;
} else if (dp[i - 1][j] >= dp[i][j - 1]) {
i--;
} else {
j--;
}
}
reverse(lcs.begin(), lcs.end());
cout << "LCS length = " << dp[n][m] << "\n";
cout << "LCS string = " << lcs;
}
```
```cpp=
// LIS
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n);
for (int i = 0; i < n; i++) cin >> a[i];
vector<int> dp(n, 1), pre(n, -1); // pre[i] 記錄 dp[i] 來源位置
int ans = 1, pos = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (a[j] < a[i] && dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
pre[i] = j;
}
}
if (dp[i] > ans) {
ans = dp[i];
pos = i;
}
}
// 反向尋找 LIS 序列
vector<int> lis;
for (int i = pos; i != -1; i = pre[i])
lis.push_back(a[i]);
reverse(lis.begin(), lis.end());
cout << "LIS length = " << ans << "\n";
cout << "LIS sequence = ";
for (int x : lis) cout << x << " ";
}
```
```cpp=
// LIS(僅長度)
#include<bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
vector<int> a(n); // 原序列
for(int i=0;i<n;i++){
cin >> a[i];
}
vector<int> b; // LIS每個位置的最小值
for(int i=0;i<n;i++){
if(b.empty() or b.back()<a[i]){ // 空序列or現在這個數比LIS最後一位還大
b.push_back(a[i]); // 插入到LIS最後方
}
else{
*lower_bound(b.begin(),b.end(),a[i])=a[i];
// 使用二分搜找到第一個大於a[i]的數字,將其替換成更小的a[i]。
}
}
cout << b.size(); // 輸出LIS長度
return 0;
}
```
```cpp=
// 最少硬幣數
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
long long n, target;
cin >> n >> target;
vector<long long> coin(n);
for (int i = 0; i < n; i++) cin >> coin[i];
const long long INF = 1e18;
vector<long long> dp(target + 1, INF);
dp[0] = 0; // 0 元不用硬幣
for (int i = 0; i < n; i++) {
for (int j = coin[i]; j <= target; j++) {
dp[j] = min(dp[j], dp[j - coin[i]] + 1);
}
}
if (dp[target] == INF) cout << -1; // 無法湊出
else cout << dp[target];
}
```
```cpp=
// 硬幣組合數
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
long long n, target;
cin >> n >> target;
vector<long long> coin(n);
for (int i = 0; i < n; i++) cin >> coin[i];
vector<long long> dp(target + 1, 0);
dp[0] = 1; // 只有一種方式湊出 0 元
for (int i = 0; i < n; i++) {
for (int j = coin[i]; j <= target; j++) {
dp[j] += dp[j - coin[i]];
}
}
cout << dp[target];
}
```
```cpp=
// 前綴和,建表O(n),查詢O(1)
vector<int> prefix(n);
prefix[0] = a[0];
for (int i = 1; i < n; ++i) {
prefix[i] = prefix[i-1] + a[i];
}
// 範例:若 a = {5, 2, 4, 1, 3}
// 則 prefix = {5, 7, 11, 12, 15}
// 查 a = {5,2,4,1,3} 中 [1,3] 的和:2+4+1 = 7
int sumLR = (L > 0 ? prefix[R] - prefix[L-1] : prefix[R]);
// sumLR = prefix[3] - prefix[0] = 12 - 5 = 7
```
```cpp=
// 後綴和
vector<int> suffix(n);
suffix[n-1] = a[n-1];
for (int i = n-2; i >= 0; --i) {
suffix[i] = suffix[i+1] + a[i];
}
// 範例:若 a = {5, 2, 4, 1, 3}
// 則 suffix = {15, 10, 8, 4, 3}
// 查 a = {5,2,4,1,3} 中 [1,3] 的和:2+4+1 = 7
int sumLR_suffix = (R+1 < n ? suffix[L] - suffix[R+1] : suffix[L]);
// = suffix[1] - suffix[4] = 10 - 3 = 7
```
```cpp=
// 快速冪
long long quick_pow(long long base, long long exp, long long mod) {
long long result = 1;
base %= mod;
while (exp > 0) {
if (exp % 2 == 1) // 奇數先提出一個base
result = (result * base) % mod;
base = (base * base) % mod; // base 平方
exp /= 2; // 指數砍半
}
return result;
}
```
## 分治
```cpp=
// 二分搜O(nlogn)
#include <bits/stdc++.h>
using namespace std;
int binarySearch(vector<int>& arr, int target) {
int left = 0, right = arr.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2; // 若不會溢位可使用(left + right) / 2
if (arr[mid] == target) return mid;
else if (arr[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1; // 找不到
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, target;
cin >> n; // 陣列大小
vector<int> arr(n);
for (int i = 0; i < n; i++) cin >> arr[i]; // 輸入陣列
cin >> target; // 目標值
int pos = binarySearch(arr, target);
if (pos != -1) cout << "Found at index " << pos << "\n";
else cout << "Not found\n";
}
```
```cpp=
// 函式庫二分搜
auto it = lower_bound(arr.begin(), arr.end(), target);
if (it != arr.end())
cout << "lower_bound = " << *it << "\n";
else
cout << "No element >= " << target << "\n";
auto it2 = upper_bound(arr.begin(), arr.end(), target);
if (it2 != arr.end())
cout << "upper_bound = " << *it2 << "\n";
else
cout << "No element > " << target << "\n";
```
```cpp=
// 逆序數對
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll merge_count(vector<ll>& a, vector<ll>& tmp, int left, int right) {
if (left >= right) return 0; // 只有一個元素,沒有反序對
int mid = (left + right) / 2;
// 遞迴計算左半與右半的反序對數
ll cnt = merge_count(a, tmp, left, mid) + merge_count(a, tmp, mid + 1, right);
int i = left, j = mid + 1, k = left;
while (i <= mid && j <= right) {
if (a[i] <= a[j]) tmp[k++] = a[i++];
else {
tmp[k++] = a[j++];
cnt += (ll)(mid - i + 1); // 左邊剩下的元素都比 a[j] 大,形成反序對
}
}
while (i <= mid) tmp[k++] = a[i++]; // 把左邊剩餘元素補上
while (j <= right) tmp[k++] = a[j++]; // 把右邊剩餘元素補上
for (int t = left; t <= right; ++t) a[t] = tmp[t]; // 回寫合併結果
return cnt;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
if (!(cin >> n)) return 0;
vector<ll> a(n), tmp(n);
for (int i = 0; i < n; i++) cin >> a[i];
cout << merge_count(a, tmp, 0, n - 1) << endl;
return 0;
}
```
## 掃描線演算法
```cpp=
// 矩陣重疊O(nlogn)
#include <bits/stdc++.h>
using namespace std;
struct Rect {
int x1, y1, x2, y2;
// 左下角(x1,y1)、右上角(x2,y2)
};
struct Event {
int x, y1, y2, id, type;
// type = +1 (enter), -1 (exit)
bool operator<(const Event& other) const {
return x < other.x; // 依照 x 小到大排序
}
};
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int N; // N 個矩形
cin >> N;
vector<Rect> rects(N);
for (int i = 0; i < N; i++) {
cin >> rects[i].x1 >> rects[i].y1 >> rects[i].x2 >> rects[i].y2;
}
// 儲存事件
vector<Event> events;
for (int i = 0; i < N; i++) {
auto [x1,y1,x2,y2] = rects[i];
events.push_back({x1, y1, y2, i, +1}); // 左邊界 在 x1
events.push_back({x2, y1, y2, i, -1}); // 右邊界 在 x2
}
sort(events.begin(), events.end()); // 排序事件
set<vector<int>> colorSets; // 存顏色組合
set<int> activeSet; // 當前活躍矩形
int prevX = events[0].x; // 上一個事件,也就是左區間
int idx = 0;
// 由左而右掃描事件
while (idx < (int)events.size()) {
int x = events[idx].x;
// 由上而下掃描活躍區間的矩陣,嘗試更新重疊組合
if (x > prevX && !activeSet.empty()) {
for (int y = 0; y < 200; y++) {
vector<int> cover;
for (int id : activeSet) {
if (rects[id].y1 <= y && y < rects[id].y2) {
cover.push_back(id);
}
}
if (!cover.empty()) {
sort(cover.begin(), cover.end()); // 排列方便 set 去重
colorSets.insert(cover);
}
}
}
// 維護活躍區間的矩陣(一次把當前 x 的所有事件處理完再往右走)
while (idx < (int)events.size() && events[idx].x == x) {
auto e = events[idx];
if (e.type == +1) {
activeSet.insert(e.id); // 加入
} else {
activeSet.erase(e.id); // 移除
}
idx++; // 下一個事件
}
prevX = x; // 維護左區間
}
cout << colorSets.size() << endl;
return 0;
}
```
## 貪心
```cpp=
// 找零問題
// 把面額從大到小排序,對每個面額取能取的最多數量,扣除金額後繼續,直到找零完成。
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int m;
long long V;
cin >> m >> V;
vector<long long> c(m);
for(int i=0;i<m;i++){
cin >> c[i];
}
sort(c.rbegin(), c.rend()); // 大到小
long long remain = V;
long long total = 0;
for(int i=0;i<m && remain>0;i++){
long long use = remain / c[i]; // 這個面額要用幾枚
total += use;
remain -= c[i] * use;
}
cout << total;
return 0;
}
```
```cpp=
// 活動選擇
// 按照結束時間小到大排序,每次選擇目前結束時間最早且與已選活動不衝突的活動。
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
vector<pair<long long,long long>> a(n);
for(int i = 0; i < n; i++)
cin >> a[i].first >> a[i].second;
// 按結束時間排序
sort(a.begin(), a.end(), [](auto &x, auto &y){
if(x.second != y.second) return x.second < y.second;
return x.first < y.first;
});
long long last = -1;
int count = 0;
for(auto &[s, f] : a){
if(s >= last){
count++;
last = f;
}
}
cout << count;
return 0;
}
```
```cpp=
// 最小合併成本
// 每次取兩條最短繩合併(最小堆),把它們的和放回堆,累加成本。
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
int n;
cin >> n;
priority_queue<long long, vector<long long>, greater<long long>> pq;
// 讀入繩子長度,放入最小堆
for(int i = 0; i < n; i++) {
long long l;
cin >> l;
pq.push(l);
}
long long total_cost = 0;
// 每次合併兩條最短的繩子
while(pq.size() > 1) {
long long a = pq.top(); pq.pop();
long long b = pq.top(); pq.pop();
long long cost = a + b;
total_cost += cost;
pq.push(cost); // 將合併後的繩子放回堆
}
cout << total_cost;
return 0;
}
```
## 並查集
```cpp=
// f(now): 查詢現在這位玩家所屬公會的代表(公會會長)
// 同時做「路徑壓縮」,讓玩家以後能更快找到自己的會長
int f(int now) {
if (p[now] == now) {
// 如果自己就是自己的會長(代表),直接回傳
return now;
} else {
// 否則,往上找到會長,並把自己直接連到會長(壓縮路徑)
return p[now] = f(p[now]);
}
}
// 初始化:一開始每位玩家都創自己的公會(自己是自己的會長)
for (int i = 0; i < n; i++) {
p[i] = i;
}
// 進行 m 次公會合併操作
for (int i = 0; i < m; i++) {
int a, b;
cin >> a >> b; // 讀入要合併的兩位玩家
a = f(a); // 找出 a 所屬的公會會長
b = f(b); // 找出 b 所屬的公會會長
if (a != b) {
// 如果他們不在同一個公會,就把 a 的公會併入 b 的公會
p[a] = b;
}
}
```
```cpp=
// 最小生成樹
#include<bits/stdc++.h>
using namespace std;
vector<int> p;
// 並查集
int f(int now){
if(p[now]==now) return now;
else{
p[now]=f(p[now]);
return p[now];
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int n,m;
typedef pair<int,pair<int,int>> node; // (A到B邊的權重,A點,B點)
while(cin >> n >> m){
int need=n-1; // n個節點的樹應該會有n-1條邊
vector<node> v;
p.resize(n);
for(int i=0;i<m;i++){
int a,b,c;
cin >> a >> b >> c;
v.push_back({c,{a,b}});
v.push_back({c,{b,a}});
}
sort(v.begin(),v.end()); // 從邊長權重最小開始考慮
// 並查集預設,每個節點都是單獨的集合
for(int i=0;i<n;i++){
p[i]=i;
}
long long sum=0;
for(auto h : v){
int x=f(h.second.first);
int y=f(h.second.second);
// 不可繞成環
if(x==y){
continue;
}
else{
p[x]=y;
need--;
sum+=h.first;
}
}
if(need==0){
cout << sum << endl;
}
else{
cout << "-1" << endl; // 無法生成MST,依題意輸出
}
}
return 0;
}
```