# 競程魔法書
## C++模板
**vimrc**
``` vim=
set encoding=utf-8
set nu
syntax on
set ai
set shiftwidth=4
set tabstop=4
set expandtab
set smartindent
set backspace=2
set ruler
set ic
set number
set mouse=a
set hlsearch
inoremap {<CR> {<CR>}<Esc>ko
inoremap " ""<Esc>i
inoremap ' ''<Esc>i
```
**模板**
```cpp=
#include <bits/stdc++.h>
using namespace std;
#define INITIO() ios_base::sync_with_stdio(false);cin.tie(NULL);cout<<setprecision(numeric_limits<double>::max_digits10+1);
#define MEM(i,j) memset(i,j,sizeof i);
#define X first
#define Y second
#define pb push_back
#define mp make_pair
#define ALL(a) a.begin(),a.end()
typedef long long ll;
typedef pair<int ,int> pii;
const double Eps = 1e-8;
const ll Inf = 1e18+7;
const int Mod = 1e9+7;
int main (){
INITIO();
int tt;
cin >> tt;
while (tt--) {
}
return 0;
}
```
## STL
### Map STL
```cpp=
map<T, T> m;
unordered_map<T, T> umap; //c++11
//放東西
m.insert();
m[] = ;
//找東西
auto iter = m.find();
if(iter != m.end())
cout<<iter->second<<endl;
```
### Queue STL
```cpp=
queue<T> q;
priority_queue<T> pq;
q.push();
q.pop();
q.front();
q.back();
```
### 排列組合 C(n,r)
>stackoverflow 來的,所以好像有overflow可能
```cpp=
long long C(int n, int r) {
if(r > n - r) r = n - r;
long long ans = 1;
int i;
for(i = 1; i <= r; i++) {
ans *= n - r + i;
ans /= i;
}
return ans;
}
```
### Sort & Cmp
>**sort** 預設升冪 , 改降冪使用 **greater<T>()**
```cpp=
bool myfunction (int a,int b) {
return (a<b);
}
sort(All(s), myfunction);
//用lambda,A是某種struct
sort( values.begin( ), values.end( ), [ ]( const A& l, const A& r )
{
return l.key < r.key;
});
```
## 快速冪
>一般的
```cpp=
ll Power(ll n,ll time){
ll a = 1;
while(time){
if(time & 1) a = a*n % Mod;
n = n*n % Mod;
time>>=1;
}
return a;
}
```
### 矩陣快速冪
>費式數列 , 比**dp**解更快
```cpp=
ll Pow(ll t){
t--;
vector<ll> a = {1,1,1,0},b = {1,1,1,0};
while(t){
vector<ll> c = a,d = b;
if(t&(ll)1){
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
a[2*i+j] = c[2*i]*b[j]+c[2*i+1]*b[2+j];
}
for(int i=0;i<2;i++)
for(int j=0;j<2;j++)
b[2*i+j] = d[2*i]*d[j]+d[2*i+1]*d[2+j];
t>>=(ll)1;
}
return a[0];
}
```
## 質數
>線性篩法
```cpp=
ll n;
cin >> n;
bool arr[n+1];
MEM(arr,true);
for(int i=2;i<=sqrt(n);++i)
if(arr[i])
for(int j = i*i ; j<=n ;j+=i)
arr[j] = false;
for(int k=1;k<=n;k++)
if(arr[k]) cout << k <<endl;
```
## 最小生成樹 MST
>求長度應用例
>Kruskal演算法 , 路徑壓縮沒啟發式合併
```cpp=
const int N = 5e3+5,M = 2e5+5;
int boss[N]={0};
struct Edge{
int a;
int b;
int val;
} e[M];
bool cmp(Edge a,Edge b){
return a.val<b.val;
}
int find(int idx){
if(boss[idx]==idx) return idx;
return boss[idx] = find(boss[idx]);
}
```
``` cpp=
int n,m,length=0,en=0;
cin>>n>>m;
for(int i=1;i<n+1;i++){
boss[i]=i;
}
for(auto &it : e){
cin>>it.a>>it.b>>it.val;
}
sort(e,e+m,cmp);
for(int j=0;j<m;j++){
if(find(e[j].a) == find(e[j].b)) continue;
boss[find(e[j].a)] = find(e[j].b);
length+=e[j].val;
en++;
}
if(en<n-1) cout << "orz"<<endl;
else cout <<length<<endl;
```
### Prim’s algorithm
```cpp=
#define MAX 10000
struct node {
int id,cost;
};
vector<node> E[MAX];
struct edge {
int u,v,cost;
bool operator<(const edge& y) const { return cost > y.cost; }
};
priority_queue<edge> Q;
bool vis[MAX];
//use
{
// init
for(int i = 0; i < n; i++)
vis[i] = false;
while(m--) {
cin >> u >> v >> cost;
E[u].push_back({v,cost});
E[v].push_back({u,cost});
}
// Prim's Algorithm
Q.push({0,0,0});
while(!Q.empty()) {
edge e = Q.top();
Q.pop();
u = e.v;
if(!vis[u]) {
ans += e.cost;
for(auto v : E[u]) {
if(!vis[v.id])
Q.push({u,v.id,v.cost});
}
}
vis[u] = true;
}
cout << ans << '\n';
}
```
## 最短路
**Floyd**
```cpp=
ll dp[Maxn][Maxn];
ll n,k,q;
cin >> n >> k >> q;
for(int i = 0;i<n ;i++){
for(int j =0;j<n ;j++){
if(i == j) dp[i][j] = 0;
else dp[i][j] = Inf;
}
}
while(k--){
ll a,b,w;
cin >> a >> b >> w;
dp[a][b] = w;
}
for(int i =0;i<n;i++)
for(int k=0;k<n;k++)
for(int j = k;j <n;j++)
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]);
```
## Dijkstra
單點搜尋
$O((E+N)log(N))$
```
struct Node{
ll n,w;
bool operator<(const node &lhs) const {
return lhs.w < w; //min heap
}
};
```
初始:
```
MEM(dist,Inf); //不連通設Inf
MEM(vis,false); //沒有拜訪
priority_queue<Node> q;
q.push((Node){1,dist[1]=0}); //看起點更改
```
循環:
```
while(q.size()){
node now = q.top();q.pop();
vis[now.n] = true;
for(auto nxt : e[now.n]){
if(vis[nxt.n]) continue;
const int &updater = dist[now.n] + nxt.w;
if(update < dist[nxt.n]) {
dist[nxt.n] = update; //鬆弛
q.push((Node){nxt.n,dist[nxt.n]});
}
}
}
```
## Bellman-Ford+檢查負環
單點搜尋
$O(NE)$
```
int w[N][N];
int d[N];
int parent[N];
void bellman_ford(int source){
for(int i=0;i<n;i++) d[i]=Inf;
d[source] = 0;
parent[source] = source;
for(int i=0;i<n-1;i++)
for(int a=0;a<n;++a)
for(int b=0;b<n;b++)
if(d[a] != Inf && w[a][b] != Inf)
if(d[a]+w[a][b] < d[b]){
d[b] = d[a] + w[a][b];
parent[b] = a;
}
}
```
負環檢查
* 如果還能鬆弛代表有負環
```
bool is_negative(){
for(int a=0;a<n;++a)
for(int b=0;b<n;++b)
if(d[a] != Inf && w[a][b] != Inf)
if(d[a]+w[a][b] < d[b])
return true;
return false;
}
```
遞迴版本 視情況修改 (一開始刻我以為是 dijkstra,但能處理處理負環)
```
ll bell(int start,int end,int val){
ll ans = Inf;
if(dis[start]<=val) return Inf;
else dis[start] = val;
if(start == end) return val;
for(int i=0;i<E[start].size();i++)
ans = min(ans,bell(E[start][i].X,end,E[start][i].Y+val));
return ans;
}
```
## Floyd-Warshell + 檢查負環
$O(N^3)$
多源搜尋
```
for(int i = 0;i<n ;i++){
for(int j =0;j<n ;j++){
if(i == j) dp[i][j] = 0;
else dp[i][j] = Inf;
}
}
...
for(int k =0;k<n;k++)
for(int i=0;i<n;i++)
for(int j = 0;j <n;j++)
dp[i][j] = min(dp[i][j],dp[i][k]+dp[k][j]);
```
負環
```
bool is_negative(){
for (int i=0; i<n; i++)
if (d[i][i] < 0) //自己到自己必須是0否則有負環
return true;
return false;
}
```
## 關節點
```cpp=
#define MAX 105
int low[MAX],dep[MAX];
bool ap[MAX];
int d = 0;
vector<int> adj[MAX];
void dfs(int u, int w == -1) {
int child{0};
low[u] = dep[u] = ++d;
for (auto& v : adj[u]) {
if (v == w) continue; // 剛剛過來的點
if (!dep[v]) { // 尚未拜訪的點
++child;
dfs(v, u);
low[u] = min(low[u], low[v]); // 更新 back edge (u 先往 child 走可能會有更好的 back edge)
if (low[v] >= dep[u]) // v 走 back edge 無法到 u 的祖先
ap[u] = true;
}
else // 已經拜訪過得的點
low[u] = min(low[u], dep[v]); // 更新 back edge (u 可能可以直接走到祖先)
}
if (w == -1)
ap[u] = (child > 1); // 檢查 root 是否為關節點 (子樹>1)
--d;
}
```
## 最低共同祖先
```cpp=
// pointer jumping technique (binary lifting)
#define MAX 30001
vector<int> node[MAX];
int DPWay[MAX][15]; /*點往上走 2^i level 所到的點,2^15=32768 > 30001*/
int treeLevel[MAX]; /*點高度*/
int treePath[MAX]; /*紀錄當前各level的點*/
void dfs(int now, int pre, int level) {
treeLevel[now] = level;
treePath[level] = now;
for(int i = 1; (1 << i) < level; i++)
DPWay[now][i] = treePath[level - (1 << i)];
for(int i = 0; i < node[now].size(); i++) {
if(node[now][i].first == pre)
continue;
DPWay[node[now][i].first][0] = now;
dfs(node[now][i].first, now, level + 1);
}
}
int LCA(int A,int B) {
int levelA = treeLevel[A], levelB = treeLevel[B];
for(int i = (int)log2(abs(levelA - levelB)); i >= 0; --i) {
if(abs(levelA - levelB) >= (1 << i)) {
if(levelA > levelB) {
A = DPWay[A][i];
levelA -= (1 << i);
}
else {
B = DPWay[B][i];
levelB -= (1 << i);
}
}
}
// A B 走到同一個高度 (假設樹高小於32,我們先看距離有沒有超過16,有的話就往上走16步;剩下的距離一定小於16,我們再看它有沒有超過8;以此類推,4,2,1,就到終點了。)
if (A == B)
return A;
for (int i = (int)log2(levelA); i >= 0; --i)
if (DPWay[A][i] != DPWay[B][i]) {
A = DPWay[A][i];
B = DPWay[B][i];
}
// u, v 一起走到 lca(u, v) 的下方
return DPWay[A][0];
// 回傳 lca(u, v)
}
```
## 線段樹
```cpp=
template <typename value_t>
class SGT {
public:
int n;
vector<value_t> t;
vector<optional<value_t>> lz;
int left(int tv) { return tv + 1; }
int right(int tv,int tl, int tm) { return tv + (tm-tl) * 2 ; }
void build(const vector<value_t>& v, int tv, int tl, int tr) {
if(tr - tl == 1) {
t[tv] = v[tl];
}
else {
int tm = (tl+tr) / 2;
build(v,left(tv),tl,tm);
build(v,right(tv,tl,tm),tm,tr);
t[tv] = merge(t[left(tv)],t[right(tv,tl,tm)]);
}
}
value_t merge(const value_t& x, const value_t& y) {
return x + y;
}
void update(int tv, int len, const value_t& x) {
if(!lz[tv])
lz[tv] = x;
else
lz[tv] = lz[tv].value() + x;
t[tv] += len * x;
}
void push(int tv, int tl, int tr) {
if(!lz[tv])
return;
int tm = (tl+tr) / 2;
update(left(tv),tm-tl,lz[tv].value());
update(right(tv,tl,tm),tr-tm,lz[tv].value());
lz[tv].reset();
}
void modify(int l, int r, const value_t& x, int tv, int tl, int tr) {
if(l == tl && r == tr)
update(tv,tr-tl,x);
else {
push(tv,tl,tr);
int tm = (tl+tr) / 2;
if(r <= tm)
modify(l,r,x,left(tv),tl,tm);
else if(l >= tm)
modify(l,r,x,right(tv,tl,tm),tm,tr);
else {
modify(l,tm,x,left(tv),tl,tm);
modify(tm,r,x,right(tv,tl,tm),tm,tr);
}
t[tv] = merge(t[left(tv)],t[right(tv,tl,tm)]);
}
}
value_t query(int l, int r, int tv, int tl, int tr) {
if(l == tl && r == tr)
return t[tv];
push(tv,tl,tr);
int tm = (tl+tr) / 2;
if(r <= tm)
return query(l,r,left(tv),tl,tm);
else if(l >= tm)
return query(l,r,right(tv,tl,tm),tm,tr);
else
return merge(query(l,tm,left(tv),tl,tm),query(tm,r,right(tv,tl,tm),tm,tr));
}
public:
SGT(const vector<value_t>& v) : n{v.size()}, t(2*n), lz(2*n) {
build(v,1,0,n);
}
void modify(int l, int r, const value_t& x) { modify(l,r,x,1,0,n); }
value_t query(int l,int r) { return query(l,r,1,0,n); }
};
// a[0] a[1] a[2]
// 1 2 3
// l = 1 r = 3 k = 5
// query(l-1,r) modify(l-1,r,k)
```
### 懶人標記
```cpp=
//新值是value,修改區間是[l,r],節點v的範圍是[L,R]
void modify(type value, int l, int r, int L, int R, vertex v){
if(l == L && r == R){
打懶標在v上;
return;
}
int M = (L + R) / 2;
if(r <= M) modify(value, l, r, L, M, v的左子節點);
else if(l > M) modify(value, l, r, M + 1, R, v的右子節點);
else{
modify(value, l, M, L, M, v的左子節點);
modify(value, M + 1, r, M + 1, R, v的右子節點);
}
用兩個子節點的答案更新v的答案;
}
```
修改
```cpp=
//查詢範圍是[l,r],節點v的區間範圍是[L,R]
type query(int l, int r, int L, int R, vertex v){
if(l == L && r == R)
return ans of v;
把節點v的懶標下推; //<----
int M = (L + R) / 2;
if(r <= M)
return query(l, r, L, M, v的左子節點);
else if(l > M)
return query(l, r, M + 1, R, v的右子節點);
else{
return 用query(l, M, L, M, v的左子節點)
和query(M + 1, r, M + 1, R, v的右子節點)
得出區間[l,r]的答案;
}
}
```
## Computation Geometry
```cpp=
struct Point{
double x, y;
Point(double x = 0, double y = 0) : x(x), y(y){}
Point operator+(const Point &b)const{ // vector addition
return Point(x + b.x, y + b.y);
}
Point operator-(const Point &b)const{ // vector subtraction
return Point(x - b.x, y - b.y);
}
Point operator*(double d)const{ // scale (multiplication)
return Point(x * d, y * d);
}
Point operator/(double d)const{ // scale (division)
return Point(x / d, y / d);
}
double dot(const Point &b)const{ // vector dot
return x * b.x + y * b.y;
}
double cross(const Point &b)const{ // vector cross
return x * b.y - y * b.x;
}
};
// premise: three points are collinear
bool btw(const Point &p1, const Point &p2, const Point &p3){
return (p1 - p3).dot(p2 - p3) <= 0;
}
bool collinearity(const Point &p1, const Point &p2, const Point &p3){
return (p1 - p3).cross(p2 - p3) == 0;
}
bool pointOnSegment(const Point &p1, const Point &p2, const Point &p3){
return collinearity(p1, p2, p3) && btw(p1, p2, p3);
}
int ori(const Point &p1, const Point &p2, const Point &p3){
double d = (p2 - p1).cross(p3 - p1);
if(d == 0)
return 0;
return d > 0 ? 1 : -1;
}
bool seg_intersect(const Point &p1, const Point &p2, const Point &p3, const Point &p4){
int a123 = ori(p1, p2, p3);
int a124 = ori(p1, p2, p4);
int a341 = ori(p3, p4, p1);
int a342 = ori(p3, p4, p2);
if(a123 == 0 && a124 == 0)
return btw(p1, p2, p3) || btw(p1, p2, p4) || btw(p3, p4, p1) || btw(p3, p4, p2);
return a123 * a124 <= 0 && a341 * a342 <= 0;
}
Point intersect(const Point &p1, const Point &p2, const Point &p3, const Point &p4){
int a123 = (p2 - p1).cross(p3 - p1);
int a124 = (p2 - p1).cross(p4 - p1);
return (p4 * a123 - p3 * a124) / (a123 - a124);
}
```
## 逆序數對
```cpp=
int merge_sort(vector<int> &a,int l,int r){
if(l==r) return 0;
int mid = (l+r)/2;
int lans = merge_sort(a,l,mid);
int rans = merge_sort(a,mid+1,r);
vector<int> b,c;
for(int i = l;i<=mid;i++) b.pb(a[i]);
for(int i = mid+1;i<=r;i++) c.pb(a[i]);
int i=0,j=0,ans=0;
for(int k=l;k<=r;k++){
int Min = 0;
if(i == b.size()) Min = c[j++];
else if(j == c.size()) Min = b[i++];
else if(b[i] <= c[j]) Min = b[i++];
else {
Min = c[j++];
ans += mid - i - l + 1;
}
a[k] = Min;
}
return lans+rans+ans;
}
```
## DP
### 01背包問題
```cpp=
int v[n];
int c[n];
int dp[101]={0};
int ans=0;
for(int i=0;i<n;i++){
cin>> v[i] >> c[i];
for(int j=100;j-v[i]>=0;j--){
dp[j] = max(dp[j],dp[j-v[i]]+c[i]);
ans = max(ans,dp[j]);
}
}
cout << ans <<endl;
```
## GCD
>GCD求解
```cpp=
int gcd(int a, int b)
{ return a? gcd(b%a, a) : b;}
```
### 貝祖等式 (Bezout’s Thm)
>對整數 **a,b** , 存在整數 **x,y** 使得 **ax+by = gcd(a,b)**
### 擴展歐基里德
令 **g = gcd(0,g) = gcd(a,b)**
```cpp=
int gcd(int a, int b, int &x, int &y) {
if(!a) {
x = 0; // x 為任意整數
y = 1;
return b;
}
int g = gcd(b%a, a, x, y);
int xp = y - b/a * x, yp = x; // p := previous
x = xp, y = yp;
return g;
}
```
**exgcd** 可求模反元素 , 但僅於 b 跟 m 互質
## Karatsuba algorithm
```cpp=
// 不要爆掉,感覺都是對的
int getLength(long long value) {
int counter = 0;
while (value != 0) {
counter++;
value /= 10;
}
return counter;
}
long long karatsuba(long long x, long long y) {
int xLength = getLength(x);
int yLength = getLength(y);
// the bigger of the two lengths
int N = (int)(fmax(xLength, yLength));
// if the max length is small it's faster to just flat out multiply the two nums
if (N < 10)
return x * y;
//max length divided and rounded up
N = (N/2) + (N%2);
long long multiplier = pow(10, N);
long long b = x/multiplier;
long long a = x - (b * multiplier);
long long d = y / multiplier;
long long c = y - (d * N);
long long z0 = karatsuba(a,c);
long long z1 = karatsuba(a + b, c + d);
long long z2 = karatsuba(b, d);
return z0 + ((z1 - z0 - z2) * multiplier) + (z2 * (long long)(pow(10, 2 * N)));
}
//>
```
## 流
```cpp=
struct MaxFlow{
struct edge{
int to, cap, flow, rev;
};
vector<edge> v[MAXN];
int s, t, dis[MAXN], cur[MAXN];
int dfs(int u, int cap){
if(u == t || !cap)
return cap;
for(int &i = cur[u]; i < v[u].size(); i++){
edge &e = v[u][i];
if(dis[e.to] == dis[u] + 1 && e.flow != e.cap){
int df = dfs(e.to, min(e.cap - e.flow, cap));
if(df){
e.flow += df;
v[e.to][e.rev].flow -= df;
return df;
}
}
}
dis[u] = -1;
return 0;
}
bool bfs(){
memset(dis, -1, sizeof(dis));
queue<int> q;
q.push(s), dis[s] = 0;
while(!q.empty()){
int tmp = q.front();
q.pop();
for(auto &u : v[tmp]){
if(!~dis[u.to] && u.flow != u.cap){
q.push(u.to);
dis[u.to] = dis[tmp] + 1;
}
}
}
return dis[t] != -1;
}
int maxflow(int _s, int _t){
s = _s, t = _t;
int flow = 0, df;
while(bfs()){
memset(cur, 0, sizeof(cur));
while(df = dfs(s, INF)){
flow += df;
}
}
return flow;
}
void addedge(int st, int ed, int cap){
v[st].push_back(edge{ed, cap, 0, v[ed].size()});
v[ed].push_back(edge{st, 0, 0, v[st].size() - 1});
}
};
```
## Matching
### 二分圖最大匹配
>也可處理二分圖最小點覆蓋問題,而最小點覆蓋的補集就是一個最大點獨立集。
>>點覆蓋是圖的一個點集合,圖上所有邊都至少有一個端點在集合之中。
點獨立集是圖的一個點集合,集合中的點兩兩不相鄰。
>最小邊覆蓋之邊數等於點數扣掉最大匹配之邊數,而最大邊獨立集就是匹配問題。
>>最小邊覆蓋即為所有邊覆蓋中邊數最少的。
邊獨立集是圖的一個邊集合,且集合中的邊兩兩不相鄰。
>又,一側接上源點,一側接上匯點,即可利用網路流來解決最大二分匹配問題、最大(小)權二分匹配問題。
```cpp=
class BipartiteMatching {
vector<bool> vis;
vector<int> mx{}, my{};
bool dfs(const vector<vector<int>>& adj, int x) {
for (auto& y : adj[x]) {
if (vis[y]) continue;
vis[y] = true;
if (my[y] == -1 || dfs(adj, my[y])) {
mx[x] = y, my[y] = x;
return true;
}
}
return false;
}
public:
pair<vector<int>, vector<int>> operator()(const vector<vector<int>>& adj, int ny) {
vis.resize(ny), mx.assign(adj.size(), -1), my.assign(ny, -1);
int c{0};
for (int x{0}; x < adj.size(); ++x) {
if (mx[x] != -1) continue;
fill(vis.begin(), vis.end(), false);
if (dfs(adj, x)) ++c;
}
return {move(mx), move(my)};
}
};
```
```cpp=
// Hopcroft–Karp algorithm - O(E√V)
class BipartiteMatching {
vector<vector<int>> adjy;
vector<int> dx, dy, mx, my;
bool dfs(const vector<vector<int>>& adjx, int y) {
for (auto& x : adjy[y]) {
if (dx[x] + 1 != dy[y]) continue;
if (mx[x] == -1 || dfs(adjx, mx[x])) {
my[y] = x, mx[x] = y;
dy[y] = -1; // remove from bfs forest
return true;
}
}
dy[y] = -1; // remove from bfs forest
return false;
}
bool bfs(const vector<vector<int>>& adjx) {
fill(dx.begin(), dx.end(), -1);
fill(dy.begin(), dy.end(), -1);
queue<int> qu{}, qu2{};
for (int x{0}; x < adjx.size(); ++x) // use all unmatched x's as roots
if (mx[x] == -1) qu.push(x), dx[x] = 0;
bool found{false};
while (!qu.empty() && !found) { // stop at the level found
while (!qu.empty()) {
auto x{qu.front()}; qu.pop();
for (auto& y : adjx[x])
if (dy[y] == -1 && my[y] != x) {
dy[y] = dx[x] + 1;
if (my[y] == -1) found = true;
else qu2.push(my[y]), dx[my[y]] = dy[y] + 1;
}
}
qu.swap(qu2);
}
return found;
}
public:
pair<vector<int>, vector<int>> operator()(const vector<vector<int>>& adjx, int ny) {
adjy.assign(ny, {});
for (int x{0}; x < adjx.size(); ++x)
for (auto& y : adjx[x]) adjy[y].push_back(x);
dx.resize(adjx.size()), dy.resize(ny), mx.assign(adjx.size(), -1), my.assign(ny, -1);
int c{0};
while (bfs(adjx)) {
for (int y{0}; y < ny; ++y)
if (my[y] == -1 && dy[y] != -1) c += dfs(adjx, y);
}
return {move(mx), move(my)};
}
};
```
### 一般圖最大匹配
```cpp=
// Edmonds' Blossom Algorithm - O(VEa(V))
class Matching {
int label{0};
vector<int> p{}, d{}, a{}, c1{}, c2{}, m{};
// (alternating tree), (unvisited: -1, even: 0, odd: 1), (auxiliary for lca), (cross edge)
/* DSU */
vector<int> dsu_p{}, dsu_sz{}, dsu_b{};
void dsu_reset() {
fill(dsu_p.begin(), dsu_p.end(), -1);
fill(dsu_sz.begin(), dsu_sz.end(), 1);
iota(dsu_b.begin(), dsu_b.end(), 0);
}
int find(int x) { // collapsing find
return dsu_p[x] == -1 ? x : dsu_p[x] = find(dsu_p[x]);
}
int base(int x) { return dsu_b[find(x)]; }
void contract(int x, int y) { // weighted union
auto rx{find(x)}, ry{find(y)};
if (rx == ry) return ;
auto b{dsu_b[rx]}; // treat x's base as new base
if (dsu_sz[rx] < dsu_sz[ry]) swap(rx, ry);
dsu_p[ry] = rx, dsu_sz[rx] += dsu_sz[ry], dsu_b[rx] = b;
}
/*******/
queue<int> qu{}; // only even vertices
void handle_one_side(int x, int y, int b) {
for (int v{base(x)}; v != b; v = base(p[v])) {
c1[v] = x, c2[v] = y, contract(b, v);
if (d[v] == 1) qu.push(v); // odd vertex -> even vertex
}
}
int lca(int u, int v) {
++label; // use next number in order not to clear a
while (true) {
if (a[u] == label) return u;
a[u] = label;
if (p[u] != -1) u = base(p[u]);
swap(u, v);
}
}
void augment(int r, int y) {
if (r == y) return ;
if (d[y] == 0) { // even vertex -> odd vertex
// check d[y] == 0 instead of d[p[y]] == 1, so (m[y], y) is in the blossom
augment(m[y], m[c1[y]]);
augment(r, m[c2[y]]);
m[c1[y]] = c2[y], m[c2[y]] = c1[y];
} else {
int x{p[y]};
augment(r, m[x]);
m[x] = y, m[y] = x;
}
}
bool bfs(const vector<vector<int>>& adj, int r) {
dsu_reset();
fill(d.begin(), d.end(), -1);
qu = {}, qu.push(r), d[r] = 0;
while (!qu.empty()) {
int x{qu.front()}; qu.pop();
for (auto& y : adj[x]) {
if (base(x) == base(y)) continue;
if (d[y] == -1) {
p[y] = x, d[y] = 1;
if (m[y] == -1) { // augmenting path
augment(-1, y);
return true;
} else {
p[m[y]] = y, d[m[y]] = 0;
qu.push(m[y]);
}
} else if (d[base(y)] == 0) { // blossom
int b{lca(base(x), base(y))};
handle_one_side(x, y, b);
handle_one_side(y, x, b);
}
}
}
return false;
}
public:
vector<int> operator()(const vector<vector<int>>& adj) {
label = 0, p.assign(adj.size(), -1), d.resize(adj.size()), a.resize(adj.size()), c1.resize(adj.size()), c2.resize(adj.size()), m.assign(adj.size(), -1);
dsu_p.resize(adj.size()), dsu_sz.resize(adj.size()), dsu_b.resize(adj.size());
int c{0};
/*
// find random greey matching first
vector<int> X(n); iota(X.begin(), X.end(), 0);
random_shuffle(X.begin(), X.end());
for (auto& x : X) {
if (m[x] != -1) continue;
for (auto& y : adj[x])
if (m[y] == -1) {
m[x] = y, m[y] = x, ++c;
break;
}
}
*/
for (int v{0}; v < adj.size(); ++v)
if (m[v] == -1 && bfs(adj, v)) ++c;
return move(m);
}
};
```
### 二分圖最大權匹配
```cpp=
// Hungarian Algorithm - O(V^3)
class WeightedBipartiteMatching {
int n;
vector<long long> lx{}, ly{};
vector<bool> vx{}, vy{};
queue<int> qu{}; // only X's vertices
vector<int> py{};
vector<long long> dy{}; // smallest (lx[x] + ly[y] - w[x][y])
vector<int> pdy{}; // & which x
vector<int> mx{}, my{};
void relax(const vector<vector<long long>>& w, int x) {
for (int y{0}; y < n; ++y)
if (lx[x] + ly[y] - w[x][y] < dy[y])
dy[y] = lx[x] + ly[y] - w[x][y], pdy[y] = x;
}
void augment(int y) {
while (y != -1) {
int x{py[y]}, yy{mx[x]};
mx[x] = y, my[y] = x;
y = yy;
}
}
bool bfs(const vector<vector<long long>>& w) {
while (!qu.empty()) {
int x{qu.front()}; qu.pop();
for (int y{0}; y < n; ++y) {
if (!vy[y] && lx[x] + ly[y] == w[x][y]) {
vy[y] = true, py[y] = x;
if (my[y] == -1) return augment(y), true;
int xx{my[y]};
qu.push(x), vx[xx] = true, relax(w, xx);
}
}
}
return false;
}
void relabel() {
long long d{numeric_limits<long long>::max()};
for (int y{0}; y < n; ++y) if (!vy[y]) d = min(d, dy[y]);
for (int x{0}; x < n; ++x) if (vx[x]) lx[x] -= d;
for (int y{0}; y < n; ++y) if (vy[y]) ly[y] += d;
for (int y{0}; y < n; ++y) if (!vy[y]) dy[y] -= d;
}
bool expand(const vector<vector<long long>>& w) { // expand one layer with new equality edges
for (int y{0}; y < n; ++y) {
if (!vy[y] && dy[y] == 0) {
vy[y] = true, py[y] = pdy[y];
if (my[y] == -1) return augment(y), true;
int xx{my[y]};
qu.push(xx), vx[xx] = true, relax(w, xx);
}
}
return false;
}
public:
pair<vector<int>, vector<int>> operator()(const vector<vector<long long>>& w) {
n = w.size(), lx.assign(n, 0), ly.assign(n, 0), vx.resize(n), vy.resize(n), py.resize(n), dy.resize(n), pdy.resize(n), mx.assign(n, -1), my.assign(n, -1);
for (int i{0}; i < n; ++i)
for (int j{0}; j < n; ++j)
lx[i] = max(lx[i], w[i][j]);
for (int x{0}; x < n; ++x) {
fill(vx.begin(), vx.end(), false);
fill(vy.begin(), vy.end(), false);
fill(dy.begin(), dy.end(), numeric_limits<long long>::max());
qu = {}, qu.push(x), vx[x] = true, relax(w, x);
while (!bfs(w)) {
relabel();
if (expand(w)) break;
}
}
return {move(mx), move(my)};
}
};
```
## 其他
### 一些 g++ 內建的 bit function
```cpp=
__builtin_clz() //count leading 0s
__builtin_ctz() //count trailing 0s
__builtin_popcount() //count bit 1
```
### 凸包、旋轉尺
```cpp=
vector<Point> convexHull(vector<Point> v){
int sz = 0;
vector<Point> ans;
sort(v.begin(), v.end(), cmp);
for(size_t i = 0; i < v.size(); i++){
while(sz >= 2 && (ans[sz - 1] - ans[sz - 2]).cross(v[i] - ans[sz - 2]) <= 0)
ans.pop_back(), sz--;
ans.push_back(v[i]), sz++;
}
for(int i = int(v.size()) - 2, t = sz + 1; i >= 0; i--){
while(sz >= t && (ans[sz - 1] - ans[sz - 2]).cross(v[i] - ans[sz - 2]) <= 0)
ans.pop_back(), sz--;
ans.push_back(v[i]), sz++;
}
if(v.size() > 1)
ans.pop_back();
return ans;
}
double rotatingCaliper(const vector<Point> &v){
double ans = 0;
for(int i = 0; j = 0; i < v.size(); i++){
while(abs2(v[i],v[j]) <= abs2(v[i], v[(j+1)%v.size()]))
j = (j+1) % v.size();
ans = max(ans, abs2(v[i], v[j]));
}
}
```