### 離散化
```cpp=
template <typename T>
auto get_unique_sorted(vector<T> arr) {
sort(arr.begin(), arr.end());
arr.erase(unique(arr.begin(), arr.end()), arr.end());
return arr;
}
template <typename T>
auto get_rank(const vector<T> &unique_sorted, const T &x) {
return lower_bound(unique_sorted.begin(), unique_sorted.end(), x) - unique_sorted.begin();
}
```
### 二分搜
```cpp=
//不符合條件的最小值
template <class Ty, class FuncTy>
Ty find_min_false(Ty L, Ty R, FuncTy check){
while (L < R) {
Ty mid = L + (R - L) / 2;
if (check(mid))
L = mid + 1;
else
R = mid;
}
return L;
}
//符合條件的最大值
template <class Ty, class FuncTy>
Ty find_max_true(Ty L, Ty R, FuncTy check){
while (L < R) {
Ty mid = L + (R - L + 1) / 2;
if (check(mid))
L = mid;
else
R = mid - 1;
}
return L;
}
```
### 矩陣計算
```cpp=
template <typename T>
struct Matrix
{
using rt = std::vector<T>;
using mt = std::vector<rt>;
using matrix = Matrix<T>;
int r, c;
mt m;
Matrix(int r, int c) : r(r), c(c), m(r, rt(c)) {}
rt &operator[](int i) { return m[i]; }
matrix operator+(const matrix &a)
{
matrix rev(r, c);
for (int i = 0; i < r; ++i)
for (int j = 0; j < c; ++j)
rev[i][j] = m[i][j] + a.m[i][j];
return rev;
}
matrix operator-(const matrix &a)
{
matrix rev(r, c);
for (int i = 0; i < r; ++i)
for (int j = 0; j < c; ++j)
rev[i][j] = m[i][j] - a.m[i][j];
return rev;
}
matrix operator*(const matrix &a)
{
matrix rev(r, a.c);
matrix tmp(a.c, a.r);
for (int i = 0; i < a.r; ++i)
for (int j = 0; j < a.c; ++j)
tmp[j][i] = a.m[i][j];
for (int i = 0; i < r; ++i)
for (int j = 0; j < a.c; ++j)
for (int k = 0; k < c; ++k)
rev.m[i][j] += m[i][k] * tmp[j][k];
return rev;
}
bool inverse()
{
Matrix t(r, r + c);
for (int y = 0; y < r; y++)
{
t.m[y][c + y] = 1;
for (int x = 0; x < c; ++x)
t.m[y][x] = m[y][x];
}
if (!t.gas())
return false;
for (int y = 0; y < r; y++)
for (int x = 0; x < c; ++x)
m[y][x] = t.m[y][c + x] / t.m[y][y];
return true;
}
};
```
### 線性篩 O(n)
```cpp=
vector<int> primes;
vector<bool> isPrime;
void find_prime(int n) {
primes.clear();
isPrime.assign(n + 1, true);
isPrime[0] = isPrime[1] = false;
for(int i = 2; i < n; ++i) {
if(isPrime[i])
primes.emplace_back(i);
for(const auto &p: primes){
if(1LL * i * p > n) break;
isPrime[i * p] = false;
if(i % p == 0) break;
}
}
}
```
### 線性篩 O(n) + 質因數分解 O(logx)
```cpp=
vector<int> primes;
vector<int> LPFs(n + 1, 1);
void find_prime(){
for(int i = 2; i < n; ++i) {
if(LPFs[i] == 1) {
LPFs[i] = i;
primes.emplace_back(i);
}
for(auto p: primes) {
if(1LL * i * p > n) break;
LPFs[i * p] = p;
if(i % p == 0) break;
}
}
}
void print_prime_factor(int x){
while(x != 1){
int factor = LPFs[x], cnt = 0;
for (; x % factor == 0; ++cnt)
x /= factor;
cout<<factor<<": "<<cnt<<endl;
}
}
```
### 拓展歐基里德
```cpp=
int exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1,y=0;
return a;
}
int gcd=exgcd(b,a%b,y,x);
y-=a/b*x;
return gcd;
}
```
### BIT
```cpp=
int lowbit(int x) { return x & -x; }
class BIT{
int n;
vector<long long> bit;
public:
void init(int _n) {
n = _n;
bit.resize(n);
for (auto&b: bit) b= 0;
}
long long query(int x) const{
long long sum= 0;
for (; x; x-= lowbit(x))
sum += bit[x];
return sum;
}
void modify(int x, int val) {
for (; x <= n; x+= lowbit(x))
bit[x] += val;
}
};
```
### 二維 BIT
```cpp=
class BIT2D {
int m;
vector<BIT> bit1D;
public:
void init(int _m, int _n) {
m = _m;
bit1D.resize(m);
for (auto&b: bit1D) b.init(_n);
}
long long query(int x, int y) const{
long long sum= 0;
for (; x; x-= lowbit(x))
sum += bit1D[x].query(y);
return sum;
}
void modify(int x, int y, int val) {
for (; x <= m; x+= lowbit(x))
bit1D[x].modify(y, val);
}
long long query2D(int x1, int y1, int x2, int y2) {
return query(x2, y2)
- query(x1 - 1, y2)
- query(x2, y1 - 1)
+ query(x1 - 1, y1 - 1);
}
};
```
### 區間修改 + 查詢 BIT
```cpp=
class RangeAddBIT {
int n;
BIT D, xD;
public:
void init(int _n) {
n = _n;
D.init(n);
xD.init(n);
}
void add(int ql, int qr, int val) { // [ql, qr] 加值
D.modify(ql, val);
xD.modify(ql, ql * val);
if (qr < n) {
D.modify(qr + 1, -val);
xD.modify(qr + 1, -(qr + 1) * val);
}
}
long long query(int x) { // 查詢 [1,x] 總和
return (x + 1) * D.query(x) - xD.query(x);
}
};
```
### 常用函式
```cpp=
#include<ext/pb_ds/assoc_container.hpp>
#include<ext/pb_ds/tree_policy.hpp>
using namespace __gnu_pbds;
typedef tree<int,null_type,less<int>,rb_tree_tag, tree_order_statistics_node_update> ordered_set;
typedef tree<int,null_type,less_equal<int>,rb_tree_tag, tree_order_statistics_node_update> ordered_multiset;
ordered_set.order_of_key(n)//找有幾個數小於n
ordered_set.find_by_order(n)//找第n小的數
#include<sstream>string s; stringstream ss(s);//ss<<input
ss.str(""),ss.clear();//clear
while(getline(ss,str,'m'))//以m分割
cout << str << endl;
int a,b; char c;
while(ss>>a>>c>>b)//處理像是-2/9+9/7
cout<<a<<" "<<c<<" "<<b<<endl;
//輸出 -2 / 9 endl 9 / 7
do {cout << s << "\n";}//排列組合(需sort)
while(next_permutation(s.begin(), s.end()));
prev_permutation()//逆向排列
#include<cstring>memset()//可用 0 -1 0x3F(1e9左右)
```
### 自訂隨機哈希(防卡常)
```cpp=
#include<chrono>
struct custom_hash {//Key只能放整數
static uint64_t splitmix64(uint64_t x) {
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}
size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM = chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}
};
```
### 快速冪 O(logn)
```cpp=
int qpow(int a,int b){
int res=1;
while(b){
if(b&1)
res=res*a%mod;
b>>=1;
a=a*a%mod;
}
return res;
}
```
### 線段樹
```cpp=
constexpr int N=5e5+5;
int n;
int seg[N<<2],tag[N<<2];
//線段樹與懶惰標記
void merge(int idx){
seg[idx]=seg[idx<<1]+seg[idx<<1|1];
//區間最大要把return x+y改成max(x,y)
}
void build(int idx=1,int l=1,int r=n){//創建線段樹
if(l==r){
cin>>seg[idx];
return;
}
int mid=(l+r)>>1;
build(idx<<1,l,mid);
build(idx<<1|1,mid+1,r);
merge(idx);
}
void push_tag(int idx,int l,int r){
if(tag[idx]){
int mid=(l+r)>>1;
seg[idx<<1]+=(mid-l+1)*tag[idx];//區間最大只需要seg[idx<<1]+=tag[idx];
seg[idx<<1|1]+=(r-mid)*tag[idx];//區間最大只需要seg[idx<<1|1]+=tag[idx];
tag[idx<<1]+=tag[idx];
tag[idx<<1|1]+=tag[idx];
tag[idx]=0;
}
}
void update_range(int ql,int qr,int val,int idx=1,int l=1,int r=n){//區間加值
if(l!=r)
push_tag(idx,l,r);
if(ql<=l&&r<=qr){
seg[idx]+=(r-l+1)*val;
tag[idx]+=val;
return;
}
int mid=(l+r)>>1;
if(qr<=mid)
update_range(ql,qr,val,idx<<1,l,mid);
else if(ql>mid)
update_range(ql,qr,val,idx<<1|1,mid+1,r);
else{
update_range(ql,qr,val,idx<<1,l,mid);
update_range(ql,qr,val,idx<<1|1,mid+1,r);
}
merge(idx);
}
int query(int ql,int qr,int idx=1,int l=1,int r=n){//區間查詢
if(l!=r)
push_tag(idx,l,r);
//ql~qr為欲查詢的範圍
if(ql<=l&&r<=qr)
return seg[idx];
int mid=(l+r)>>1;
if(mid>=qr)
return query(ql,qr,idx<<1,l,mid);
else if(ql>mid)
return query(ql,qr,idx<<1|1,mid+1,r);
else
return query(ql,qr,idx<<1,l,mid)+query(ql,qr,idx<<1|1,mid+1,r);
//區間最大要把return x+y改成max(x,y)
}
void update(int pos,int val,int idx=1,int l=1,int r=n){//單點更新
if(l==r){
seg[idx]=val;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)
update(pos,val,idx<<1,l,mid);
else
update(pos,val,idx<<1|1,mid+1,r);
merge(idx);
}
```
### 01背包問題 O(n)
```cpp=
for(int i=1;i<=n;i++){
for(int j=x;j>=w[i];j--){
dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
}
}
```
### 無限背包問題 O(n)
```cpp=
for(int i=1;i<=W;i++){ // 從小到大遍歷重量
for(int j=1;j<=n;j++){ // 遍歷每個物品
if(i>=w[j]) dp[i]=max(dp[i],dp[i-w[j]]+v[j]);
}
}
```
### LIS 最長遞增子序列
```cpp=
for(const auto &i:num){
if(ans.empty()||ans.back()<=i)
ans.push_back(i);
else
*upper_bound(all(ans),i)=i;
}
```
### LIS 最長遞增子序列(DP為求得LIS實際子序列)
```cpp=
for(int i=0;i<num.size();i++){
if(lis.empty()||lis.back()<num[i]){
lis.push_back(num[i]);
dp[i]=lis.size();
}
else{
auto iter=lower_bound(all(lis),num[i]);
dp[i]=iter-lis.begin()+1;
*iter=num[i];
}
}
int length=lis.size();
for(int i=num.size()-1;i>=0;i--){
if(dp[i]==length){
ans.push_back(num[i]);
length--;
}
}
```
### 拓樸排序 O(V+E)
```cpp=
void Topological_sorting(){
queue<int> que;
for(int i=1;i<=n;i++){
if(!in[i]){
que.push(i);
}
}
while(!que.empty()){
int cur=que.front();
que.pop();
ans.push_back(cur);
for(const auto &i:out[cur]){
in[i]--;
if(!in[i])
que.push(i);
}
}
}
```
### 樹直徑
```cpp=
constexpr int N = 2e5 + 5;
vi routes[N];
int D1[N], D2[N]; // 最遠、次遠距離
int ans = 0; // 直徑長度
void DFS(int u = 1, int parent = -1) {
D1[u] = D2[u] = 0;
for (int v : routes[u]) {
if (v == parent) continue;
DFS(v, u);
int dis = D1[v] + 1;
if(dis > D1[u]){
D2[u] = D1[u];
D1[u] = dis;
}
else
D2[u] = max(D2[u], dis);
}
ans = max(ans, D1[u] + D2[u]);
}
```
### 樹重心
```cpp=
constexpr int MAXN = 2e5 + 5;
vi routes[MAXN];
int subtree_size[MAXN], ans, n;
void DFS(int cur = 1, int last = -1){
subtree_size[cur] = 1;
int max_subtree = 0;
for(const auto &i: routes[cur]){
if(i == last)
continue;
DFS(i, cur);
subtree_size[cur] += subtree_size[i];
max_subtree = max(max_subtree, subtree_size[i]);
}
max_subtree = max(max_subtree, n - subtree_size[cur]);
if(max_subtree <= n / 2)
ans = cur;
}
```
### 並查集
```cpp=
int f(int cur){
return num[cur]==cur?cur:num[cur]=f(num[cur]);
}
void merge(int a,int b){
a=f(a),b=f(b);
if(a!=b){
num[b]=a;
}
}
for(int i=0;i<n;i++)//初始化
num[i]=i;
```
### 並查集(連通塊與最大連接數)
```cpp=
int f(int cur){
return num[cur]<0?cur:num[cur]=f(num[cur]);
}
void connect(int a,int b){
a=f(a),b=f(b);
if(a!=b){
num[a]+=num[b];
mx=max(mx,-num[a]);
num[b]=a;
heap--;
}
}
```
### 最小生成樹 MST
```cpp=
sort(all(cost),cmp);//a.cost<b.cost
for(const auto &i:cost){
int a=f(i.a),b=f(i.b);//使用一般並查集
if(a!=b){
num[b]=a;
counter--;
ans+=i.cost;
}
}
```
### Dijkstra
```cpp=
void Dijkstra(){
priority_queue<pii,vector<pii>,greater<>> pq;
pq.push({0,1}),ans[1]=0;
while(!pq.empty()){
pii cur=pq.top();
pq.pop();
if(cur.first>ans[cur.second])
continue;
for(const auto &i:routs[cur.second]){
if(ans[i.second]>cur.first+i.first){
ans[i.second]=cur.first+i.first;
pq.push({ans[i.second],i.second});
}
}
}
}
```
### Bellman Ford
```cpp=
struct Edge{
int start, end, cost;
};
vll Bellman_Ford(const vector<Edge> &routes, int n, int start = 1){
vll ans(n + 1, LONG_LONG_MAX);
ans[start] = 0;
auto relax = [&](const Edge &a){
if(ans[e.start] != LONG_LONG_MAX && ans[a.end] > ans[a.start] + a.cost){
ans[a.end] = ans[a.start] + a.cost;
return 1;
}
return 0;
};
for(int t = 1; t <= n; t++){
bool update = 0;
for(const auto &e: routes)
update |= relax(e);
if(t == n && update)
return {};//有負權環
}
return ans;
}
```
### Floyd Warshall(DP)
```cpp=
void Floyd_warshall(){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
for(int k=1;k<=n;k++){
dp[j][k]=min(dp[j][k],dp[j][i]+dp[i][k]);
}
}
}
}
```
### 單調隊列(求最大最小)
```cpp=
deque<int> mx,mn;
for(int i=0;i<n;i++){
if(!mx.empty()&&i-mx.front()>=window)
mx.pop_front();
if(!mn.empty()&&i-mn.front()>=window)
mn.pop_front();
while(!mx.empty()&&num[mx.back()]<num[i])
mx.pop_back();
while(!mn.empty()&&num[mn.back()]>num[i])
mn.pop_back();
mx.push_back(i),mn.push_back(i);
if(i>=window-1){
ans_max.push_back(num[mx.front()]);
ans_min.push_back(num[mn.front()]);
}
}
```
### 字串編輯距離(DP)
```cpp=
for(int i=1;i<=a;i++){
for(int j=1;j<=b;j++){
if(s1[i-1]==s2[j-1])
dp[i][j]=dp[i-1][j-1];
else
dp[i][j]=min({dp[i-1][j],dp[i][j-1],dp[i-1][j-1]})+1;
}
}
```
### LCS 最長公共子序列(DP)
```cpp=
for(int i=1;i<=s1.size();i++){
for(int j=1;j<=s2.size();j++){
if(s1[i-1]==s2[j-1])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
```
### 二元樹(以preorder和inorder建成)
```cpp=
class Node{
public:
char data;
Node* left;
Node* right;
Node(char value){
data=value;
right=nullptr;
right=nullptr;
}
};
class Tree{
public:
Node* root;
Tree(){
root=nullptr;
}
Node* build_tree(vector<char>& preorder,vector<char>& inorder){
if(preorder.empty()||inorder.empty()){
return nullptr;
}
Node* point=new Node(preorder[0]);
auto findit=find(all(inorder),preorder[0]);
int length=findit-inorder.begin();
vector<char> inorder_left(inorder.begin(),findit);
vector<char> inorder_right(findit+1,inorder.end());
vector<char> preorder_left(preorder.begin()+1,preorder.begin()+1+length);
vector<char> preorder_right(preorder.begin()+1+length,preorder.end());
point->left=build_tree(preorder_left,inorder_left);
point->right=build_tree(preorder_right,inorder_right);
return point;
}
void Postorder(Node* x){
if(x){
Postorder(x->left);
Postorder(x->right);
cout<<x->data;
}
}
void del_tree(Node* x){
if(x==nullptr)
return;
del_tree(x->left);
del_tree(x->right);
delete x;
}
};
```
### 線段樹上二分搜
```cpp=
void st_bsearch(int pos,int idx=1,int l=1,int r=n){
if(l==r&&seg[idx]>=pos){
seg[idx]-=pos;
ans=l;
return;
}
int mid=(l+r)>>1;
if(seg[idx<<1]>=pos)
st_bsearch(pos,idx<<1,l,mid);
else if(seg[idx<<1|1]>=pos)
st_bsearch(pos,idx<<1|1,mid+1,r);
merge(idx);
}
```
### KMP演算法
```cpp=
string a; // 文本串
string b; // 模板串(将被匹配的子串)
int kmp_next[N]; // next数组
void getNext(int m=b.size()){//初始化
int j = 0;
kmp_next[0] = 0;
for(int i=1; i<m; ++i){
while(j>0 && b[i]!=b[j]) j=kmp_next[j-1];
if(b[i]==b[j]) ++j;
kmp_next[i] = j;
}
}
int kmp(int n=a.size(),int m=b.size()){//使用KMP寻找匹配位置
int i, j = 0;
int p = -1;
getNext(m);
for(i=0; i<n; ++i){
while(j>0 && b[j]!=a[i]) j=kmp_next[j-1];
if(b[j]==a[i]) ++j;
if(j==m){
p = i - m + 1;
break;
}
}
return p;
}
int kmp(int n=a.size(),int m=b.size()){//使用KMP计算匹配次数
int i, j = 0, res = 0;
getNext(m);
for(i=0; i<n; ++i){
while(j>0 && b[j]!=a[i]) j=kmp_next[j-1];
if(b[j]==a[i]) ++j;
if(j==m) ++res;
}
return res;
}
```
### 動態開點+懶惰標記線段樹
```cpp=
//註解部分為修改為 max 要改動的地方
class Node {
public:
Node* left;
Node* right;
int l;
int r;
int mid;
int v;
int add;
Node(int l, int r) {
this->l = l;
this->r = r;
this->mid = (l + r) >> 1;
this->left = this->right = nullptr;
v = add = 0;
}
};
class SegmentTree {
private:
Node* root;
public:
SegmentTree() {
root = new Node(1, 1e9);
}
void modify(int l, int r, int v) {
modify(l, r, v, root);
}
void modify(int l, int r,int v, Node* node) {
if(node->l>r||node->r<l) return;
if (node->l >= l && node->r <= r)
{
node->v += (node->r - node->l + 1) * v;//node->v += v;
node->add += v;
return;
}
pushdown(node);
modify(l, r, v, node->left);
modify(l, r, v, node->right);
merge(node);
}
int query(int l, int r) {
return query(l, r, root);
}
int query(int l, int r, Node* node) {
if(node->l>r||node->r<l) return 0;//return INT_MIN;
if (node->l >= l && node-> r <= r) return node->v;
pushdown(node);
return query(l, r, node->left)+query(l, r, node->right);//取max
}
void merge(Node* node) {
node->v = node->left->v+node->right->v;//取max,就改到這
}
void pushdown(Node* node) {
if (!node->left) node->left = new Node(node->l, node->mid);
if (!node->right) node->right = new Node(node->mid + 1, node->r);
if (node->add)
{
Node* left = node->left;
Node* right = node->right;
left->v += node->add;
left->add += node->add;
right->v += node->add;
right->add += node->add;
node->add = 0;
}
}
};
```
### SPFA(用於判斷負權環)
```cpp=
vector<pii> routs[N];
int dis[N],n,cnt[N];
bool SPFA(const int &start){
fill(dis,dis+n,1e9);
memset(cnt,0,sizeof(cnt));
dis[start]=0;
queue<int> que;
bitset<N> in_que;
que.push(start);
while(!que.empty()){
int cur=que.front();
que.pop();
cnt[cur]++;
if(cnt[cur]>n){
return 1;
}
in_que[cur]=0;
for(const auto &i:routs[cur]){
if(dis[i.second]>dis[cur]+i.first){
dis[i.second]=dis[cur]+i.first;
if(!in_que[i.second]){
que.push(i.second);
in_que[i.second]=1;
}
}
}
}
return 0;
}
```
### 稀疏表
```cpp=
constexpr int N=500;
int dp[N+5][32];
int a[N]={1,2,3,4,5};
//稀疏表 idx 從 1 開始
void build(){
for(int i=1;i<=N;i++)
dp[i][0]=a[i-1];
for(int k=1;k<32;k++){
for(int i=1;i+(1<<(k-1))<=N;i++){
dp[i][k]=max(dp[i][k-1],dp[i+(1<<(k-1))][k-1]);
}
}
}
int query(int l,int r){
int idx=__lg(r-l+1);
return max(dp[l][idx],dp[r-(1<<idx)+1][idx]);
}
```
### Z-Function
```cpp=
string s="123_123456789";
const int n=s.size();
vi z(n);
for (int i = 1, l = 0, r = 0; i < n; ++i) {
if (z[i - l] < r - i + 1)
z[i] = z[i - l];
else {
z[i] = max(r - i + 1, 0);
while (i + z[i] < n && s[z[i]] == s[i + z[i]])
z[i]++;
l = i, r = i + z[i] - 1;
}
}
```
### Trie
```cpp=
struct TrieNode {
int nxt[26]; //遇到a~z時的節點編號(idx)
int cnt; //以此結尾的字串個數
} node[1000005];
int idx = 2; //root = 1
void insert(string s){
int cur = 1; //從root開始
for(auto i:s) {
if(node[cur].nxt[i-'a'] == 0){
node[cur].nxt[i-'a'] = idx; //開一個新的node
cur = idx;
idx++;
}
else {
cur = node[cur].nxt[i-'a'];
}
}
node[cur].cnt++;
}
int query(string s){
int cur = 1;
for(auto i:s) {
if(node[cur].nxt[i-'a'] == 0){
return 0;
}
else {
cur = node[cur].nxt[i-'a'];
}
}
return node[cur].cnt;
}
```