模板库 ~ Template library

2023-05-18,,

TOC

建议使用 Ctrl+F 搜索 .

目录
小工具 / C++ Tricks
NOI Linux 1.0
快速读入 / 快速输出
简易小工具
无序映射器
简易调试器
文件 IO
位运算
Smart Double
数论
GCD
快速幂相关
分数模板
EI 的取模还原分数
逆元
整除分块
线性筛
扩展欧几里得算法 (exgcd)
类欧几里得算法
中国剩余定理 (CRT) & exCRT
BSGS & exBSGS
积性函数筛子
组合计数
组合数取模
伯努利数
斯特林数
Catalan 数
Cantor 展开
LGV 引理
矩阵树定理
线性代数
矩阵类
图论
Simple Graph
单源最短路径 (SSSP)
多源最短路径
最短路图
k 短路
同余最短路
欧拉图 / 欧拉路
一般图支配树
最小生成树 (MST)
连通性 (Tarjan)
2-SAT
Floyd
二分图
图的匹配
Prufer 序列
网络流
DAG 上问题
树上问题 - 最近公共祖先 (LCA)
树上问题
树上问题 - 树同构判定
字符串
字符串 Hash
Trie 相关
Manacher
KMP
Border Theory
Z Algorithm
AC 自动机
后缀数组 (SA)
后缀树 (ST)
后缀平衡树
后缀自动机 (SAM)
回文自动机 (PAM)
最小表示法
数据结构
链表
单调数据结构
ST 表
树状数组
线段树
平衡树
珂朵莉树 (ODT)
k-D Tree
动态树
持久化数据结构
动态规划
背包 DP
多项式
多项式乘法
计算几何
其他问题
高精度
偏序
悬线法
自适应辛普森积分
牛顿迭代
模拟退火 (SA)
Fibonacci 数列
邪门算法

Updating...

语言:C++ .

因为时间错乱,所以 Code Style 混乱 .

未注明作者的都是自己写的 .

小工具 / C++ Tricks

NOI Linux 1.0

bashrc

title x:设置标题为 x
compile x:编译 x.cpp 并写入到可执行文件 x
compilerun xcompile x 并运行 x

function title() {
if [[ -z "$ORIG" ]]; then
ORIG=$PS1
fi
TITLE="\[\e]2;$*\a\]"
PS1=${ORIG}${TITLE}
} compile() {
g++ $1.cpp -lm -O2 -Wall -Wextra -std=c++14 -fsanitize=address,undefined -o $1
} compilerun() {
compile $1
./$1
}

Bash 命令:

source ~/.bashrc:快速应用 bashrc
alias x=y:设置 y 的别名为 x,等号没有空格 .
unalias x:取消 x 这个别名

vimrc

注意插入前先按 i!!

https://www.cnblogs.com/fushao2yyj/p/8404511.html,tabstop 改成 4 .

nanorc

注意反人类 nano 的快捷键

nano 快捷键

保存:Ctrl + O 保存 .
复制一整行:Alt + 6
剪贴一整行:Ctrl + K
复制/剪贴多行或者一行中的一部分:先将光标移动到需要复制/剪贴的文本的开头,按 Ctrl + 6Alt + A 做标记,然后移动光标到待复制/剪贴的文本末尾 . 这时选定的文本会反白,然后按复制一行的方法复制 . Ctrl + 6 取消 .
粘贴:Ctrl + U
搜索:Ctrl + W 然后输入搜索单词,Alt + W 跳转下一个
退出:Ctrl + X,然后输入 Y 表示保存修改,N 不保存 . Ctrl + C 取消 .

Ref. https://zhuanlan.zhihu.com/p/259380222 .

set tabsize 4       # 设置制表符宽度
set autoindent # 允许自动缩进
set cut # 设置 CTRL-K 可以剪贴到行末
set noconvert # 不要转换 DOS/UNIX 换行符
set nowrap # 不要自动换行
set nohelp # 不显示下面两行帮助 (Alt+X 显示)
set morespace # 隐藏标题下的空白行,换取更多编辑空间
set smooth # 平滑卷屏
set suspend # 允许 ctrl-z 将 nano 置于后台
set smarthome # 第一次 Home 跳到行首非空字符,第二次到行首
set tabstospaces # 展开制表符为空格(如果需要的话)
set mouse # 允许鼠标
# set linenumbers # 显示行号(可以在编辑时 ALT-# 切换),NOI Linux 1.0 的 nano 太旧了不支持!
set backupdir path # 设置备份路径
set backup # 允许保存备份
set casesensitive # 搜索使用大小写敏感
set multibuffer # 使用 CTRL-r 读取文件时,默认读取到新缓存
set nonewlines # 不在文件末尾添加新行

快速读入 / 快速输出

快速读入 / 快速输出

简易小工具

will update .

Tools
namespace __TYPE__
{
typedef unsigned short u16;
typedef short i16;
typedef unsigned u32;
typedef int i32;
typedef unsigned long long u64;
typedef long long i64;
typedef unsigned long long u64;
typedef i64 ll;
typedef u64 ull;
};
template<typename T>
inline T chkmax(T& x, const T& y){if (x < y) x = y; return x;}
template<typename T>
inline T chkmin(T& x, const T& y){if (x > y) x = y; return x;}

无序映射器

pool (Mixed)

接口:

构建 pool : T -> unsigned 的映射 .

若 \(M\) 为 pool<T> 类型变量,满足若 \(x\neq y\),则 \(\operatorname{get}(x)\neq\operatorname{get}(y)\) .

template<typename T>
class pool
{
unordered_map<T, unsigned> pol;
unsigned cc;
public:
pool(){cc = 0;}
~pool() = default;
unsigned get(T x)
{
auto ptr = pol.find(x);
if (ptr == pol.end()){pol[x] = ++cc; return cc;}
else return ptr -> second;
}
unsigned size()const{return cc;}
void clear(){cc = 0; pol.clear();}
};

简易调试器

普通版

输出至 stderr,检测 ONLINE_JUDGE 宏来控制调试开关 .

#ifdef ONLINE_JUDGE
#define dputs
#define dout
#define dprintf
#else // debug 至 stderr .
#define dputs(x) fputs(x"\n", stderr)
#define dprintf(F...) fprintf(stderr, F)
#define dout cerr
#endif
来自 C++ Tricks
#define error(args...) { string _s = #args; replace(_s.begin(), _s.end(), ',', ' '); stringstream _ss(_s); istream_iterator<string> _it(_ss); err(_it, args); }

void err(istream_iterator<string> it) {}
template<typename T, typename... Args>
void err(istream_iterator<string> it, T a, Args... args) {
cerr << *it << " = " << a << endl;
err(++it, args...);
}

文件 IO

文件 IO
#define ifile(x) freopen(x".in", "r", stdin)
#define ofile(x) freopen(x".out", "w", stdout)
#define file(x) {ifile(x); ofile(x);}
#define ifile(x) freopen(#x".in", "r", stdin)
#define ofile(x) freopen(#x".out", "w", stdout)
#define file(x) {ifile(x); ofile(x);}

位运算

BITWISE
namespace BITWISE
{
using __TYPE__ :: i64;
inline int ctz(int x){return __builtin_ctz(x);}
inline int clz(int x){return __builtin_clz(x);}
inline int ffs(int x){return __builtin_ffs(x);}
inline int popcnt(int x){return __builtin_popcount(x);}
inline int lowbit(int x){return x & -x;}
inline i64 ctzl(i64 x){return __builtin_ctzll(x);}
inline i64 clzl(i64 x){return __builtin_clzll(x);}
inline i64 ffsl(i64 x){return __builtin_ffsll(x);}
inline i64 popcntl(i64 x){return __builtin_popcountll(x);}
inline i64 lowbitl(i64 x){return x & -x;}
}

Smart Double

Smart Double

数论

GCD

数论只会 GCD

辗转相除法
inline int gcd(int a, int b){return b ? gcd(b, a%b) : a;}
Stein 算法

基于值域预处理的快速 GCD
// i don't know that.

快速幂相关

log 快速幂
inline int qpow(int a, int n)
{
int ans = 1;
while (n)
{
if (n & 1) ans = 1ll * ans * a % P;
a = 1ll * a * a % P; n >>= 1;
} return ans;
}
线性筛(指数不变 O(1) 求幂)
inline void linear_sieve(int n)
{
notprime[1] = true; power[1] = 1;
for (int i=2;i<=n;i++)
{
if (!notprime[i]){plist.emplace_back(i); power[i] = qpow(i, k);}
for (auto x : plist)
{
int now = i*x;
if (now > n) break;
notprime[now] = true;
if (!(i%x)){power[now] = 1ll * power[i] * power[x] % P; break;}
power[now] = 1ll * power[i] * power[x] % P;
}
}
}
光速幂(底数不变 O(1) 求幂)
// 值域 2^31-1
template<int MOD>
struct FastPow
{
private:
ll p1[66666], p2[66666];
static ll qpow(ll a,ll n){/**/}
public:
explicit FastPow(ll a)
{
ll t1=a,t2=qpow(t1,65536); p1[0]=p2[0]=1;
for (int i=1; i<65536; i++) p1[i] = p1[i-1] * t1 % MOD;
for (int i=1; i<65536; i++) p2[i] = p2[i-1] * t2 % MOD;
}
ll operator()(unsigned n){return p2[n>>16] % MOD * p1[n&65535] % MOD;}
};

分数模板类

EI 的取模还原分数

取模还原分数 (by Elegia)

https://blog.csdn.net/EI_Captain/article/details/117172239

pair<int, int> approx(int p, int q, int A) {
int x = q, y = p, a = 1, b = 0;
while (x > A) {
swap(x, y); swap(a, b);
a -= x / y * b;
x %= y;
}
return make_pair(x, a);
}

逆元

模素数单个逆元

任意模数单个逆元 (exgcd)
ll inv(ll a, ll P){ll x ,y, d = exgcd(a ,P, x, y); return (d == 1) ? x % P : -1;}
线性求一行逆元

整除分块

一维整除分块

二维整除分块

线性筛

线性筛素数、欧拉函数、莫比乌斯函数
inline void linear_sieve(int n)
{
notprime[1] = true; phi[1] = mu[1] = 1;
for (int i=2; i<=n; i++)
{
if (!notprime[i]){plist.emplace_back(i); phi[i] = i-1; mu[i] = -1;}
for (auto x : plist)
{
int now = i*x;
if (now > n) break;
notprime[now] = true;
if (!(i%x)){phi[now] = phi[i] * x; mu[now] = 0; break;}
phi[now] = phi[i] * phi[x]; mu[now] = mu[i] * mu[x];
}
}
}

扩展欧几里得算法 (exgcd)

传引用的 exgcd
ll exgcd(ll a,ll b,ll& x,ll& y)
{
if (!b){x = 1; y = 0; return a;}
ll _ = exgcd(b, a%b, x, y), old_x = x, old_y = y;
x = old_y; y = old_x - (a/b) * old_y;
return _;
}
返回 pair 的 exgcd

类欧几里得算法

中国剩余定理 (CRT) & exCRT

BSGS & exBSGS

积性函数筛子

组合计数

组合数取模

杨辉三角
预处理阶乘及其逆元
Lucas
exLucas
using namespace std;
typedef long long ll;
ll qpow(ll a,ll n,ll p){/**/}
namespace extra_lucas_detail
{
ll fac(ll n,ll p,ll pk)
{
if (!n) return 1;
ll ans=1;
for (int i=1;i<pk;i++)
if (i%p) ans=ans*i%pk;
ans=qpow(ans,n/pk,pk);
for (int i=1; i<=n%pk;i++)
if (i%p) ans=ans*i%pk;
return ans*fac(n/p,p,pk)%pk;
}
ll exgcd(ll a,ll b,ll& x,ll& y){/**/}
ll inv(ll a,ll P){ll x,y,d=exgcd(a,P,x,y); return (d==1)?x%P:-1;}
ll C(ll n,ll m,ll p,ll pk)
{
if (n<m) return 0;
ll A=fac(n,p,pk),B=fac(m,p,pk),C=fac(n-m,p,pk),cnt=0;
for (ll i=n;i;i/=p) cnt+=i/p;
for (ll i=m;i;i/=p) cnt-=i/p;
for (ll i=n-m;i;i/=p) cnt-=i/p;
return A*inv(B,pk)%pk*inv(C,pk)%pk*qpow(p,cnt,pk)%pk;
}
}
ll exlucas(ll n,ll m,ll p)
{
using namespace extra_lucas_detail;
ll ans=0,P=p;
for (int i=2;(p>1)&&(i*i<=p);i++)
{
ll pk=1;
while (!(p%i)){p/=i; pk*=i;}
if (pk>1)
{
ll now=C(n,m,i,pk);
ans=(ans+now*(P/pk)%P*inv(P/pk,pk)%P)%P;
}
}
if (p>1)
{
ll now=C(n,m,p,p);
ans=(ans+now*(P/p)%P*inv(P/p,p)%P)%P;
} return (ans%P+P)%P;
}

伯努利数

斯特林数

Catalan 数

Cantor 展开

LGV 引理

矩阵树定理

线性代数

矩阵类

图论

Simple Graph

Simple Graph
template<typename T>
struct Graph
{
typedef vector<pair<int, T> > _;
_ g[N];
inline void addedge(int u, int v, ll w){g[u].emplace_back(make_pair(v, w));}
inline void ade(int u, int v, ll w){addedge(u, v, w); addedge(v, u, w);}
inline _ edges(const unsigned& id)const{return g[id];}
inline _& edges(const unsigned& id){return g[id];}
inline _ operator [] (const unsigned& id)const{return edges(id);}
inline _& operator [] (const unsigned& id){return edges(id);}
inline void clear(){for (int i=0; i<N; i++) g[i].clear();}
};

单源最短路径 (SSSP)

无负权:

Dijkstra 堆优化
struct Node
{
int idx, d;
Node(int _=0, int __=0){idx = _; d = __;}
inline bool operator <(const Node& a)const{return d>a.d;}
};
void dijkstra(int s)
{
memset(dis, 0x3f, sizeof dis);
memset(vis, false, sizeof vis);
priority_queue<Node> q; dis[s]=0; q.push(Node(s, dis[s]));
while (!q.empty())
{
auto now = q.top(); q.pop();
int u = now.idx;
if (vis[u]) continue;
vis[u] = true;
for (auto _ : g[u])
{
int v = _.first, w = _.second;
if (vis[v]) continue;
if (dis[v] > dis[u]+w){dis[v] = dis[u]+w; q.push(Node(v, dis[v]));}
}
}
}

有负权 / 判负环:

Bellman-Ford

Bellman-Ford:

// a 存边
bool bellman_ford(int s)
{
memset(dis, 0x3f, sizeof dis);
dis[s] = 0; int t = 0;
for (int i=1; i<n; i++)
for (int j=0; j<m; j++)
{
int u = a[j].u, v = a[j].v, w = a[j].w;
if (dis[v] > dis[u] + w) dis[v] = dis[u] + w;
}
for (int i=0; i<m; i++)
{
int u = a[j].u, v = a[j].v, w = a[j].w;
if (dis[v] > dis[u] + w) return false;
} return true;
}

SPFA:

// 入队次数超过 n 则有负环
inline void spfa(int x)
{
memset(dis, 0x3f, sizeof dis); memset(vis, 0, sizeof vis);
queue<int> q; vis[x] = true; dis[x] = 0; q.push(x);
while (!q.empty())
{
int u = q.front(); q.pop();
for (auto e : g[u])
{
int v = e.first, w = e.second;
if (dis[v] > dis[u] + w){dis[v] = dis[u] + w; if (!vis[v]) q.push(v), vis[v] = true;}
} vis[now] = false;
}
}
EI Magic (by Elegia)

https://www.cnblogs.com/Elegia/p/16122335.html

Bonus: 差分约束 .

多源最短路径

Floyd 全源最短路

Johnson 全源最短路

最短路图

最短路图

假装求最短路的函数是 SSSP(Graph, distance, u) .


k 短路

A*

持久化可并堆

同余最短路

跳楼机

给定 \(x,y,z,h\),问有多少个 \(k\in[1,h]\) 使得 \(k=px+qy+rz\),其中 \(p,q,r\) 是变量 .

using namespace std;
const int N=1e5+500;
typedef unsigned long long ull;
typedef long long ll;
ll x,y,z,h,dis[N];
bool vis[N];
typedef vector<pair<ll,ll> > graph[N];
graph g;
inline void addedge(ll u,ll v,ll w){g[u].push_back(make_pair(v,w));}
struct node
{
int idx; ll dis;
node(int _=0,ll __=0){idx=_; dis=__;}
bool operator <(const node& u)const{return dis>u.dis;}
};
void dijkstra(int s){/**/}
int main()
{
scanf("%lld%lld%lld%lld",&h,&x,&y,&z);
if ((x==1)||(y==1)||(z==1)){printf("%lld",h); return 0;}
if (x>y) swap(x,y);
if (y>z) swap(y,z);
if (x>z) swap(x,z);
if (x>y) swap(x,y);
if (y>z) swap(y,z);
if (x>z) swap(x,z);
for (int i=0;i<x;i++) addedge(i,(i+y)%x,y),addedge(i,(i+z)%x,z);
dijkstra(0); ll ans=0;
for (int i=0;i<x;i++)
if (h>=dis[i]) ans+=(h-dis[i]-1)/x+1;
printf("%lld",ans);
return 0;
}

欧拉图 / 欧拉路

欧拉图判定

找一条欧拉路

一般图支配树

一般图支配树

https://www.luogu.com.cn/record/54432641

using namespace std;
typedef long long ll;
const int N=205000,M=20*N;
int n,m;
int head[N],shead[N],ihead[N],rhead[N],ver[M],nxt[M],tot=-1;
int siz[N],father[N],fa[N];
int dfn[N],id[N],low[N],sdom[N],idom[N],cnt;
void addedge(int head[],int u,int v){ver[++tot]=v; nxt[tot]=head[u]; head[u]=tot;}
void init(int n){for (int i=1;i<=n;i++) sdom[i]=low[i]=fa[i]=i;}
void dfs1(int u)
{
dfn[u]=++cnt; id[cnt]=u;
for (int i=head[u];~i;i=nxt[i])
{
int v=ver[i];
if (dfn[v]) continue;
father[v]=u; dfs1(v);
}
}
int get(int u)
{
if (u==fa[u])return u;
int root=get(fa[u]);
if (dfn[sdom[low[fa[u]]]]<dfn[sdom[low[u]]]) low[u]=low[fa[u]];
return fa[u]=root;
}
void tarjan()
{
int v;
for (int i=cnt;i>1;i--)
{
int u=id[i];
for (int j=rhead[u];~j;j=nxt[j])
{
v=ver[j];
if (!dfn[v]) continue;
get(v);
if (dfn[sdom[low[v]]]<dfn[sdom[u]]) sdom[u]=sdom[low[v]];
}
addedge(shead,sdom[u],u);
fa[u]=father[u];
u=father[u];
for (int j=shead[u];~j;j=nxt[j])
{
v=ver[j]; get(v);
if (sdom[low[v]]==u) idom[v]=u;
else idom[v]=low[v];
}
shead[u]=-1;
}
for (int i=2;i<=cnt;i++)
{
int u=id[i];
if (idom[u]!=sdom[u]) idom[u]=idom[idom[u]];
}
}
void dfs2(int u)
{
siz[u]=1;
for(int i=ihead[u];~i;i=nxt[i])
{
int v=ver[i];
dfs2(v);
siz[u]+=siz[v];
}
}
int main()
{
memset(head,-1,sizeof head);
memset(ihead,-1,sizeof ihead);
memset(rhead,-1,sizeof rhead);
memset(shead,-1,sizeof shead);
scanf("%d%d",&n,&m);
for (int i=1,u,v;i<=m;i++){scanf("%d%d",&u,&v); addedge(head,u,v); addedge(rhead,v,u);}
init(n);
dfs1(1);
tarjan();
for (int i=2;i<=n;i++)
if (idom[i]) addedge(ihead,idom[i],i);
dfs2(1);
for (int i=1;i<=n;i++) printf("%d ",siz[i]);
return 0;
}

最小生成树 (MST)

Kruskal

Prim

Boruvka

连通性 (Tarjan)

2-SAT

Floyd

全源最短路

全源最短路 - Floyd .

传递闭包
bool dp[N][N];
void Floyd()
{
for (int k=1; k<=n; k++)
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
dp[i][j] = dp[i][j] | (dp[i][k] & dp[k][j]);
}
最小环

二分图

图的匹配

Prufer 序列

网络流

DAG 上问题

拓扑排序

支配树

https://www.luogu.com.cn/record/54416499

int lca(int p1,int p2)
{
if (dep[p1]<dep[p2]) swap(p1,p2);
for (int i=18;i>=0;i--)
if (dep[f[p1][i]]>=dep[p2]) p1=f[p1][i];
if (p1==p2) return p2;
for (int i=18;i>=0;i--)
if (f[p1][i]!=f[p2][i]) p1=f[p1][i],p2=f[p2][i];
return f[p1][0];
}
void topsort()
{
queue<int> q;
for (int i=1;i<=n;i++)
if (!deg[i]) q.push(i),father[i]=0;
while (!q.empty())
{
int u=q.front(),fa=father[u]; q.pop();
addedge(shead,fa,u);
f[u][0]=fa; dep[u]=dep[fa]+1;
for (int i=1;i<=22;i++) f[u][i]=f[f[u][i-1]][i-1];
for (int i=head[u];~i;i=nxt[i])
{
int v=ver[i]; --deg[v];
if (!deg[v]) q.push(v);
if (father[v]==-1) father[v]=u;
else father[v]=lca(father[v],u);
}
}
}

树上问题 - 最近公共祖先 (LCA)

倍增 LCA
int n,m,s,dep[N],f[N][LgN+10];
bool vis[N];
inline void dfs(int now,int fa)
{
f[now][0]=fa; dep[now]=dep[fa]+1; int s=g[now].size();
for (int i=1;i<=LgN;i++) f[now][i]=f[f[now][i-1]][i-1];
for (int i=0;i<s;i++)
if (g[now][i]!=fa) dfs(g[now][i],now);
}
inline int lca(int p1,int p2)
{
if (dep[p1]<dep[p2]) swap(p1,p2);
for (int i=18;i>=0;i--)
if (dep[f[p1][i]]>=dep[p2]) p1=f[p1][i];
if (p1==p2) return p2;
for (int i=18;i>=0;i--)
if (f[p1][i]!=f[p2][i]) p1=f[p1][i],p2=f[p2][i];
return f[p1][0];
}
Tarjan 算法离线 LCA

树剖求 LCA
namespace LCA
{
int fa[N], dep[N], siz[N], son[N], top[N], dfn[N], rnk[N], cc;
void dfs1(int u)
{
siz[u] = 1;
for (auto v : g[u])
{
if (fa[u] == v) continue;
dep[v] = dep[u] + 1;
fa[v] = u;
dfs1(v);
siz[u] += siz[v];
if (!son[u]|| (siz[v] > siz[son[u]])) son[u] = v;
}
}
void dfs2(int u, int t)
{
top[u] = t;
rnk[++cc] = u; dfn[u] = cc;
if (!son[u]) return ;
dfs2(son[u], t);
for (auto v : g[u])
if ((v != son[u]) && (v != fa[u])) dfs2(v, v);
}
int lca(int u, int v)
{
while (top[u] != top[v])
{
if (dep[top[u]] > dep[top[v]]) u = fa[top[u]];
else v = fa[top[v]];
}
return dep[u] > dep[v] ? v : u;
}
}

树上问题

树的直径与重心

树链剖分 (HLD)

LOJ #139

树上问题 - 树同构判定

字符串

字符串 Hash

BKDR Hash

Trie 相关

普通 Trie

01 Trie

压缩 Trie

Manacher

Manacher

KMP

KMP
typedef char str[N];
typedef long long ll;
str s1, s2;
int l1, l2, nxt[N], f[N];
void getnxt()
{
nxt[1] = 0; int j = 0;
for (int i=2; i<=l2; i++)
{
while (j && (s2[i] != s2[j+1])) j = nxt[j];
if (s2[i] == s2[j+1]) ++j;
nxt[i] = j;
}
}
void kmp()
{
int j = 0;
for (int i=1; i<=l1; i++)
{
while ((j==l2) || (j && (s1[i] != s2[j+1]))) j = nxt[j];
if (s1[i] == s2[j+1]) ++j;
f[i] = j;
}
}

Border Theory

Border 树
// 上接 KMP
getnxt();
for (int i=1; i<=l; i++) G.ade(i, nxt[i]);

Z Algorithm

Z Algorithm

AC 自动机

AC Automaton
// Sig 字符集(
// 此处字符集为小写字母('a' ~ 'z')
const int N = 1e6 + 50, Sig = 26;
struct AC
{
int tr[N][Sig], fail[N], mark[N], cc, root;
inline void insert(string s)
{
int u = root, l = s.length();
for (int i=0; i<l; i++)
{
if (!tr[u][s[i] - 'a']) tr[u][s[i] - 'a'] = ++cc;
u = tr[u][s[i] - 'a'];
} ++mark[u];
}
inline void build()
{
queue<int> q; fail[root] = root;
for (int i=0; i<Sig; i++)
{
if (tr[root][i]){q.push(tr[root][i]); fail[tr[root][i]] = root;}
else tr[root][i] = root;
}
while (!q.empty())
{
int u = q.front(); q.pop();
for (int i=0; i<Sig; i++)
{
if (tr[u][i]){fail[tr[u][i]] = tr[fail[u]][i]; q.push(tr[u][i]);} // trie
else tr[u][i] = tr[fail[u]][i]; // automaton
}
}
}
inline int query(string s)
{
int u = root, ans = 0, l = s.length();
for (int i=0; i<l; i++)
{
u = tr[u][s[i] - 'a'];
for (int j = u; j && ~mark[j]; j = fail[j]){ans += mark[j]; mark[j] = -1;}
} return ans;
}
inline void clear(){memset(tr, 0, sizeof tr); memset(mark, 0, sizeof mark); cc = 0;}
AC(){root = cc = 0;}
}ac;
AC Automaton + fail 树
const int N = 1e6 + 50, Sig = 26;
int n;inline int trans(const char& c){return c - 'a';}
struct SimpleGraph
{
vector<int> g[N];
inline void addedge(int u,int v){g[u].emplace_back(v);}
inline void ade(int u,int v){addedge(u, v); addedge(v, u);}
vector<int>& operator [](const int& idx){return g[idx];}
inline vector<int>& out_edges(int u){return g[u];}
};
struct AC
{
SimpleGraph FT; // fail tree
int tr[N][Sig], fail[N], ed[N], siz[N], cc, root;
inline void insert(int id, string s)
{
int u = root, l = s.length();
for (int i=0; i<l; i++)
{
int _ = trans(s[i]);
if (!tr[u][_]) tr[u][_] = ++cc;
u = tr[u][_]; ++siz[u];
} ed[id] = u;
}
inline void build()
{
queue<int> q;
for (int i=0; i<Sig; i++)
{
if (tr[root][i]){q.push(tr[root][i]); fail[tr[root][i]] = root;}
else tr[root][i] = root;
}
while (!q.empty())
{
int u = q.front(); q.pop();
for (int i=0; i<Sig; i++)
{
if (tr[u][i]){q.push(tr[u][i]); fail[tr[u][i]] = tr[fail[u]][i];}
else tr[u][i] = tr[fail[u]][i];
} FT.addedge(fail[u], u);
}
}
void dfs(int u){for (int v : FT[u]) dfs(v), siz[u] += siz[v];}
inline void query(string s){dfs(0);}
AC(){cc = root = 0;}
}ac;

后缀数组 (SA)

后缀树 (ST)

后缀平衡树

后缀自动机 (SAM)

回文自动机 (PAM)

最小表示法

数据结构

链表

链表
// 我不会链表

单调数据结构

单调栈

单调队列

ST 表

ST 表
// 传入一个 Functor 给 op,要求:
// 1. op(x, x) = x (可重复贡献)
// 2. op(x, op(y, z)) = op(op(x, y), z) (结合律)
template<typename T, typename op>
struct ST
{
typedef unsigned U;
T f[N][LgN+10];
template<typename rit>
inline void reset(rit fst, rit sec)
{
const U n = sec - fst;
for (U i=1; i<=n; i++) f[i][0] = *(fst + (i-1));
for (U j=1; (1u<<j)<=n; j++)
for (U i=1; i+(1u<<j)-1u<=n; i++) f[i][j] = op()(f[i][j-1], f[i + (1<<(j-1))][j-1]);
}
inline T query(U l, U r)const{U _ = log2(r-l+1); return op()(f[l][_], f[r-(1<<_)+1][_]);}
template<typename rit>
ST(rit fst, rit sec){init(fst, sec);}
ST() = default;
~ST() = default;
};
ST 表,但是查询最值位置
// 传入一个 Functor 给 op,要求:
// 1. op(x, x) = x (可重复贡献)
// 2. op(x, op(y, z)) = op(op(x, y), z) (结合律)
template<typename T, typename op>
struct ST
{
typedef unsigned U;
T f[N][LgN+10]; U pos[N][LgN+10];
template<typename rit>
inline void reset(rit fst, rit sec)
{
const U n = sec - fst;
for (U i=1; i<=n; i++){f[i][0] = *(fst + (i-1)); pos[i][0] = i;}
for (U j=1; (1u<<j)<=n; j++)
for (U i=1; i+(1u<<j)-1u<=n; i++)
{
if (op()(f[i][j-1], f[i+(1<<(j-1))][j-1])){f[i][j] = f[i][j-1]; pos[i][j] = pos[i][j-1];}
else{f[i][j] = f[i+(1<<(j-1))][j-1]; pos[i][j] = pos[i+(1<<(j-1))][j-1];}
}
}
inline T query(U l, U r)const{U _ = log2(r-l+1); return op()(f[l][_], f[r-(1<<_)+1][_]);}
inline U querypos(U l, U r)const{U _ = log2(r-l+1);return op()(f[l][_], f[r-(1<<_)+1][_]) ? pos[l][_] : pos[r-(1<<_)+1][_];}
template<typename rit>
ST(rit fst, rit sec){init(fst, sec);}
ST() = default;
~ST() = default;
};

树状数组

树状数组 - 单点加区间查

树状数组 - 区间加单点查

树状数组 - 区间加区间查

线段树

普通线段树 (Segment Tree)

区间赋值,区间 min .

struct SMT
{
#define ls u << 1
#define rs u << 1 | 1
struct Node{int l, r, m, lazy;}tr[4*N];
inline void pushup(int u){tr[u].m = min(tr[ls].m, tr[rs].m);}
inline void pushdown(int u)
{
if (!tr[u].lazy) return ;
tr[ls].m = tr[rs].m = tr[u].lazy;
tr[ls].lazy = tr[rs].lazy = tr[u].lazy;
tr[u].lazy = 0;
}
void build(int u, int l, int r)
{
tr[u].l = l; tr[u].r = r;
if (l == r){tr[u].m = w[rnk[l]]; return ;}
int mid = (l + r) >> 1;
build(ls, l, mid); build(rs, mid+1, r);
pushup(u);
}
void change(int u, const int& l, const int& r, const int& x)
{
int L = tr[u].l, R = tr[u].r;
if ((l <= L) && (R <= r)){tr[u].m = tr[u].lazy = x; return ;}
pushdown(u);
int mid = (L + R) >> 1;
if (l <= mid) change(ls, l, r, x);
if (r > mid) change(rs, l, r, x);
pushup(u);
}
int query(int u, const int& l, const int& r)
{
int L = tr[u].l, R = tr[u].r;
if ((l <= L) && (R <= r)) return tr[u].m;
pushdown(u);
int ans = INF, mid = (L + R) >> 1;
if (l <= mid) chkmin(ans, query(ls, l, r));
if (r > mid) chkmin(ans, query(rs, l, r));
return ans;
}
#undef rs
#undef ls
}T;
线段树合并

雨天的尾巴

using namespace std;
const int N = 1e5+50;
typedef long long ll;
typedef pair<int, int> pii;
template<typename T>
inline int chkmin(T& a, const T& b){if (a > b) a = b; return a;}
template<typename T>
inline int chkmax(T& a, const T& b){if (a < b) a = b; return a;}
int n, m, root[N], ans[N];
vector<int> g[N], G;
inline void addedge(int u, int v){g[u].emplace_back(v);}
inline void ade(int u, int v){addedge(u, v); addedge(v, u);}
struct Query{int x, y, z;}q[N];
namespace LCA{/*树剖 LCA*/}
struct Node //
{
int p, cnt;
Node(int _, int __) : p(_), cnt(__){}
Node() = default;
bool operator < (const Node& rhs)const{return (cnt == rhs.cnt) ? p > rhs.p : cnt < rhs.cnt;}
Node operator + (const Node& rhs)const{return Node(max(p, rhs.p), cnt+rhs.cnt);}
Node& operator += (const Node& rhs){return *this = *this + rhs;}
};
struct SMF
{
int ch[40*N][2], cc;
Node v[40*N];
inline void pushup(int u){v[u] = max(v[ch[u][0]], v[ch[u][1]]);}
inline void insert(int& u, int l, int r, Node _)
{
if (!u) u = ++cc;
if (l == r){v[u] += _; return ;}
int mid = (l + r) >> 1, p = _.p;
if (p <= mid) insert(ch[u][0], l, mid, _);
else insert(ch[u][1], mid+1, r, _);
pushup(u);
}
inline void merge(int& x, int y, int l, int r) // this m
{
if (!x || !y){x += y; return ;}
if (l == r){v[x] += v[y]; return ;}
int mid = (l + r) >> 1;
merge(ch[x][0], ch[y][0], l, mid);
merge(ch[x][1], ch[y][1], mid+1, r);
pushup(x);
}
}T;
vector<Node> mdf[N];
int R;
void dfs(int u)
{
for (int v : g[u])
if (v != LCA::fa[u]){dfs(v); T.merge(root[u], root[v], 1, R);}
for (auto e : mdf[u]) T.insert(u, 1, R, e);
if (T.v[u].p) ans[u] = G[T.v[u].p - 1];
}
inline int discrete(int w){return lower_bound(G.begin(), G.end(), w) - G.begin()+1;}
int main()
{
using namespace LCA;
scanf("%d%d", &n, &m);
for (int i=1, u, v; i<n; i++){scanf("%d%d", &u, &v); ade(u, v);}
dep[1] = 1; dfs1(1); dfs2(1, 1);
for (int i=1; i<=m; i++) scanf("%d%d%d", &q[i].x, &q[i].y, &q[i].z), ++q[i].z, G.emplace_back(q[i].z);
stable_sort(G.begin(), G.end());
G.erase(unique(G.begin(), G.end()), G.end());
R = G.size()+1;
for (int i=1; i<=m; i++)
{
int x = q[i].x, y = q[i].y, z = discrete(q[i].z), _ = lca(x, y);
mdf[x].emplace_back(Node(z, 1)); mdf[y].emplace_back(Node(z, 1));
mdf[_].emplace_back(Node(z, -1)); mdf[fa[_]].emplace_back(Node(z, -1));
}
for (int i=1; i<=n; i++) T.insert(root[i], i, i, Node());
dfs(1);
for (int i=1; i<=n; i++) printf("%d\n", max(0, ans[i]-1));
return 0;
}
线段树分裂
李超线段树 (Li-chao Tree)

HEOI2013 Segment

using namespace std;
const int N=2e5+500;
typedef pair<double,int> pdi;
struct line{double k,b;}p[N];
int s[N],cnt,n;
double calc(int id,int d){return p[id].b+p[id].k*d;}
void add(int x0,int y0,int x1,int y1)
{
++cnt;
if (x0==x1){p[cnt].k=0; p[cnt].b=max(y0,y1);}
else{p[cnt].k=1.0*(y1-y0)/(x1-x0); p[cnt].b=y0-p[cnt].k*x0;}
}
void update(int root,int cl,int cr,int l,int r,int u)
{
int v=s[root],mid=(cl+cr)>>1;
bool UgV=calc(u,mid)>calc(v,mid);
if ((r<cl)||(cr<l)) return ;
if ((l<=cl)&&(cr<=r))
{
if (cl==cr) // leaf
{
if (UgV) s[root]=u;
return ;
}
if (p[v].k<p[u].k)
{
if (UgV){s[root]=u; update(root<<1,cl,mid,l,r,v);}
else update(root<<1|1,mid+1,cr,l,r,u);
}
else if (p[v].k>p[u].k)
{
if (UgV){s[root]=u; update(root<<1|1,mid+1,cr,l,r,v);}
else update(root<<1,cl,mid,l,r,u);
}
else{if (p[u].b>p[v].b) s[root]=u;}
return ;
}
update(root<<1,cl,mid,l,r,u);
update(root<<1|1,mid+1,cr,l,r,u);
}
pdi Max(const pdi& A,const pdi& B)
{
if (A.first==B.first) return A.second>B.second?B:A;
else return A.first>B.first?A:B;
}
pdi query(int root,int l,int r,int d)
{
if ((r<d)||(d<l)) return make_pair(0.0,0);
int mid=(l+r)>>1; pdi ans=make_pair(calc(s[root],d),s[root]);
if (l==r) return ans;
return Max(ans,Max(query(root<<1,l,mid,d),query(root<<1|1,mid+1,r,d)));
}
int main()
{
scanf("%d",&n); int opt,x0,y0,x1,y1,k,lastans=0;
while (n--)
{
scanf("%d",&opt);
if (!opt)
{
scanf("%d",&k); k=(k+lastans-1+39989)%39989+1; // no UB
printf("%d\n",lastans=query(1,1,39989,k).second);
}
else
{
scanf("%d%d%d%d",&x0,&y0,&x1,&y1);
x0=(x0+lastans-1+39989)%39989+1; x1=(x1+lastans-1+39989)%39989+1;
y0=(y0+lastans-1+1000000000)%1000000000+1;
y1=(y1+lastans-1+1000000000)%1000000000+1;
if (x0>x1) swap(x0,x1),swap(y0,y1);
add(x0,y0,x1,y1);
update(1,1,39989,x0,x1,cnt);
}
} return 0;
}
吉司机线段树 (Segment Tree beats!)

兔队线段树 (楼房重建)

从《楼房重建》出发浅谈一类使用线段树维护前缀最大值的算法

using namespace std;
const int N=1e5+500,M=1<<18|7;
typedef long long ll;
int n,q,id[M],cnt[M];
ll h[N];
bool gt(int p1,int p2)
{
if (!p2) return h[p1];
return h[p1]*p2>h[p2]*p1;
}
void build(int u,int l,int r)
{
id[u]=l; cnt[u]=1;
if (l==r) return ;
int mid=(l+r)>>1;
build(u<<1,l,mid); build(u<<1|1,mid+1,r);
}
int calc(int u,int l,int r,int p)
{
if (l==r) return gt(l, p);
int mid=(l+r)>>1;
if (gt(id[u<<1],p)) return calc(u<<1,l,mid,p)+cnt[u];
else return calc(u<<1|1,mid+1,r,p);
}
void modify(int u,int l,int r,int p)
{
if (l==r) return ;
int mid=(l+r)>>1;
if (p<=mid) modify(u<<1,l,mid,p);
else modify(u<<1|1,mid+1,r,p);
id[u]=gt(id[u<<1|1],id[u<<1])?id[u<<1|1]:id[u<<1];
cnt[u]=calc(u<<1|1,mid+1,r,id[u<<1]);
}
int main()
{
scanf("%d%d",&n,&q); build(1,1,n);
int p,x;
while (q--)
{
scanf("%d%d",&p,&x);
h[p]=x; modify(1,1,n,p);
printf("%d\n",calc(1,1,n,0));
}
}

平衡树

普通平衡树:

AVL 树
using namespace std;
const int N=1e5+15;
struct AVLTree
{
private:
struct Node{int l,r,val,height,size;}avl[N];
int cnt,root;
inline void newnode(int &now,int val){avl[now=++cnt].val=val; avl[cnt].size=1;}
inline void update(int now)
{
avl[now].size=avl[avl[now].l].size+avl[avl[now].r].size+1;
avl[now].height=std::max(avl[avl[now].l].height,avl[avl[now].r].height)+1;
}
inline int BF(int now){return avl[avl[now].l].height-avl[avl[now].r].height;}
inline void lrotate(int &now)
{
int r=avl[now].r; avl[now].r=avl[r].l; avl[r].l=now; now=r;
update(avl[now].l); update(now);
}
inline void rrotate(int &now)
{
int l=avl[now].l; avl[now].l=avl[l].r; avl[l].r=now; now=l;
update(avl[now].r); update(now);
}
inline void check(int &now)
{
int nf=BF(now);
if (nf>1)
{
int lf=BF(avl[now].l);
if (lf>0) rrotate(now);
else lrotate(avl[now].l),rrotate(now);
} else if (nf<-1)
{
int rf=BF(avl[now].r);
if(rf<0) lrotate(now);
else rrotate(avl[now].r),lrotate(now);
} else if (now) update(now);
}
void ins(int &now,int val)
{
if (!now) newnode(now,val);
else if(val<avl[now].val) ins(avl[now].l,val);
else ins(avl[now].r,val);
check(now);
}
int find(int &now,int fa)
{
int ret;
if (!avl[now].l){ret=now; avl[fa].l=avl[now].r;}
else{ret=find(avl[now].l,now); check(now);}
return ret;
}
void del(int &now,int val)
{
if (val==avl[now].val)
{
int l=avl[now].l,r=avl[now].r;
if (!l||!r) now=l+r;
else
{
now=find(r,r);
if (now!=r) avl[now].r=r;
avl[now].l=l;
}
}
else if (val<avl[now].val) del(avl[now].l,val);
else del(avl[now].r,val);
check(now);
}
public:
inline void ins(int val){ins(root,val);}
inline void del(int val){del(root,val);}
int getrank(int val)
{
int now=root,rank=1;
while (now)
{
if (val<=avl[now].val) now=avl[now].l;
else{rank+=avl[avl[now].l].size+1; now=avl[now].r;}
} return rank;
}
int getval(int rank)
{
int now=root;
while (now)
{
if (avl[avl[now].l].size+1==rank) break;
else if (avl[avl[now].l].size>=rank) now=avl[now].l;
else{rank-=avl[avl[now].l].size+1; now=avl[now].r;}
} return avl[now].val;
}
inline int pre(int x){return getval(getrank(x)-1);}
inline int nxt(int x){return getval(getrank(x+1));}
}BST;
Splay

Zig-Zag:

using namespace std;
const int N=1e5+15;
struct ZigZag_SplayTree
{
private:
struct Node{int l,r,val,size,cnt;}spl[N];
int cnt,root;
inline void newnode(int& now,int& val){spl[now=++cnt].val=val; ++spl[cnt].size; ++spl[cnt].cnt;}
inline void update(int now){spl[now].size=spl[spl[now].l].size+spl[spl[now].r].size+spl[now].cnt;}
inline void zig(int& now)
{
int l=spl[now].l; spl[now].l=spl[l].r; spl[l].r=now; now=l;
update(spl[now].r); update(now);
}
inline void zag(int& now)
{
int r=spl[now].r; spl[now].r=spl[r].l; spl[r].l=now; now=r;
update(spl[now].l); update(now);
}
void Splay(int x,int& y)
{
if (x==y) return ;
int& l=spl[y].l,&r=spl[y].r;
if (x==l) zig(y);
else if(x==r) zag(y);
else
{
if (spl[x].val<spl[y].val)
{
if (spl[x].val<spl[l].val) Splay(x,spl[l].l),zig(y),zig(y);
else Splay(x,spl[l].r),zag(l),zig(y);
}
else
{
if (spl[x].val>spl[r].val) Splay(x,spl[r].r),zag(y),zag(y);
else Splay(x,spl[r].l),zig(r),zag(y);
}
}
}
inline void delnode(int now)
{
Splay(now,root);
if (spl[now].cnt>1) spl[now].size--,spl[now].cnt--;
else if (spl[root].r)
{
int p=spl[root].r;
while (spl[p].l) p=spl[p].l;
Splay(p,spl[root].r); spl[spl[root].r].l=spl[root].l; root=spl[root].r; update(root);
}
else root=spl[root].l;
}
void ins(int& now,int& val)
{
if (!now) newnode(now,val),Splay(now,root);
else if (val<spl[now].val) ins(spl[now].l,val);
else if (val>spl[now].val) ins(spl[now].r,val);
else{++spl[now].size; ++spl[now].cnt; Splay(now,root);}
}
void del(int now,int& val)
{
if (spl[now].val==val) delnode(now);
else if (val<spl[now].val) del(spl[now].l,val);
else del(spl[now].r,val);
}
public:
inline void ins(int val){ins(root,val);}
inline void del(int val){del(root,val);}
int getrank(int val)
{
int now=root,rank=1;
while (now)
{
if (spl[now].val==val){rank+=spl[spl[now].l].size; Splay(now,root); break;}
if (val<=spl[now].val) now=spl[now].l;
else{rank+=spl[spl[now].l].size+spl[now].cnt; now=spl[now].r;}
} return rank;
}
int getval(int rank)
{
int now=root;
while (now)
{
int lsize=spl[spl[now].l].size;
if (lsize+1<=rank&&rank<=lsize+spl[now].cnt){Splay(now,root); break ;}
else if (lsize>=rank) now=spl[now].l;
else{rank-=lsize+spl[now].cnt; now=spl[now].r;}
} return spl[now].val;
}
inline int pre(int x){return getval(getrank(x)-1);}
inline int nxt(int x){return getval(getrank(x+1));}
}BST;
替罪羊树
using namespace std;
const int N=1e5+15;
struct tzy
{
private:
static constexpr double alpha=0.75;
struct Node
{
int l,r,val;
int size,fact;
bool exist;
}tr[N];
int cnt,root;
inline void newnode(int &now,int val){now=++cnt; tr[now].val=val; tr[now].size=tr[now].fact=1; tr[now].exist=true;}
inline bool imbalence(int now){return (max(tr[tr[now].l].size,tr[tr[now].r].size)>tr[now].size*alpha)||
(tr[now].size-tr[now].fact>tr[now].size*0.3); }
vector<int> v;
void ldr(int now)
{
if (!now) return;
ldr(tr[now].l);
if (tr[now].exist) v.push_back(now);
ldr(tr[now].r);
}
void lift(int l,int r,int &now)
{
if (l==r){now=v[l]; tr[now].l=tr[now].r=0; tr[now].size=tr[now].fact=1; return ;}
int m=(l+r)>>1;
while ((l<m)&&(tr[v[m]].val==tr[v[m-1]].val)) --m;
now=v[m];
if (l<m) lift(l,m-1,tr[now].l);
else tr[now].l=0;
lift(m+1,r,tr[now].r);
tr[now].size=tr[tr[now].l].size+tr[tr[now].r].size+1;
tr[now].fact=tr[tr[now].l].fact+tr[tr[now].r].fact+1;
}
void rebuild(int &now)
{
v.clear(); ldr(now);
if (v.empty()){now=0; return ;}
lift(0,v.size()-1,now);
}
void update(int now,int end)
{
if (!now) return;
if (tr[end].val<tr[now].val) update(tr[now].l,end);
else update(tr[now].r,end);
tr[now].size=tr[tr[now].l].size+tr[tr[now].r].size+1;
}
void check(int &now,int end)
{
if (now==end) return;
if (imbalence(now)){rebuild(now); update(root,now); return ;}
if (tr[end].val<tr[now].val) check(tr[now].l,end);
else check(tr[now].r,end);
}
void ins(int &now,int val)
{
if (!now){newnode(now,val); check(root,now); return ;}
++tr[now].size; ++tr[now].fact;
if (val<tr[now].val) ins(tr[now].l,val);
else ins(tr[now].r,val);
}
void del(int now,int val)
{
if (tr[now].exist&&tr[now].val==val){tr[now].exist=false; tr[now].fact--; check(root,now); return ;}
tr[now].fact--;
if (val<tr[now].val) del(tr[now].l,val);
else del(tr[now].r,val);
}
public:
inline void ins(int val){ins(root,val);}
inline void del(int val){del(root,val);}
int getrank(int val)
{
int now=root,rank=1;
while (now)
{
if (val<=tr[now].val) now=tr[now].l;
else {rank+=tr[now].exist+tr[tr[now].l].fact; now=tr[now].r;}
} return rank;
}
int getval(int rank)
{
int now=root;
while (now)
{
if (tr[now].exist&&tr[tr[now].l].fact+tr[now].exist==rank) break;
else if (tr[tr[now].l].fact>=rank) now=tr[now].l;
else {rank-=tr[tr[now].l].fact+tr[now].exist; now=tr[now].r;}
} return tr[now].val;
}
inline int pre(int x){return getval(getrank(x)-1);}
inline int nxt(int x){return getval(getrank(x+1));}
}BST;
fhq-Treap
using namespace std;
const int N=1e5+15;
static mt19937 rnd(19260817);
struct fhq_treap
{
private:
struct Node{int l,r,val,size,key;}fhq[N];
int cnt,root;
inline int newnode(int val){fhq[++cnt].val=val; fhq[cnt].key=rnd(); fhq[cnt].size=1; return cnt;}
inline void update(int now){fhq[now].size=fhq[fhq[now].l].size+fhq[fhq[now].r].size+1;}
void split(int now,int val,int& x,int& y)
{
if (!now) x=y=0;
else
{
if (fhq[now].val<=val){x=now; split(fhq[now].r,val,fhq[now].r,y);}
else{y=now; split(fhq[now].l,val,x,fhq[now].l);}
update(now);
}
}
int merge(int x,int y)
{
if (!x||!y) return x+y;
if (fhq[x].key>fhq[y].key){fhq[x].r=merge(fhq[x].r,y); update(x); return x;}
else{fhq[y].l=merge(x,fhq[y].l); update(y); return y;}
return 0;
}
#define def1 static int x,y;
#define def2 static int x,y,z;
public:
inline void ins(int val){def1; split(root,val,x,y); root=merge(merge(x,newnode(val)),y);}
inline void del(int val)
{
def2;
split(root,val,x,z); split(x,val-1,x,y);
y=merge(fhq[y].l,fhq[y].r); root=merge(merge(x,y),z);
}
inline int getrank(int val){def1; split(root,val-1,x,y); int ans=fhq[x].size+1; root=merge(x,y); return ans;}
inline int getval(int rank)
{
int now=root;
while (now)
{
if (fhq[fhq[now].l].size+1==rank) break;
else if (fhq[fhq[now].l].size>=rank) now=fhq[now].l;
else{rank-=fhq[fhq[now].l].size+1; now=fhq[now].r;}
} return fhq[now].val;
}
inline int pre(int val)
{
def1;
split(root,val-1,x,y); int now=x;
while (fhq[now].r) now=fhq[now].r;
int ans=fhq[now].val; root=merge(x,y); return ans;
}
inline int nxt(int val)
{
def1;
split(root,val,x,y); int now=y;
while (fhq[now].l) now=fhq[now].l;
int ans=fhq[now].val; root=merge(x,y); return ans;
}
#undef def1
#undef def2
}BST;
Treap

SBT
using namespace std;
const int N=2e5+5;
typedef long long ll;
struct SBT
{
private:
struct node{int l,r,val,size;}sbt[N];
int cnt,root;
inline void lrotate(int& now)
{
int r=sbt[now].r;
sbt[now].r=sbt[r].l; sbt[r].l=now;
sbt[r].size=sbt[now].size; sbt[now].size=sbt[sbt[now].l].size+sbt[sbt[now].r].size+1;
now=r;
}
inline void rrotate(int& now)
{
int l=sbt[now].l;
sbt[now].l=sbt[l].r; sbt[l].r=now;
sbt[l].size=sbt[now].size; sbt[now].size=sbt[sbt[now].l].size+sbt[sbt[now].r].size+1;
now=l;
}
void maintain(int& t,bool fl)
{
if (!fl)
{
if (sbt[sbt[sbt[t].l].l].size>sbt[sbt[t].r].size) rrotate(t);
else if (sbt[sbt[sbt[t].l].r].size>sbt[sbt[t].r].size) lrotate(sbt[t].l),rrotate(t);
else return ;
}
else
{
if (sbt[sbt[sbt[t].r].r].size>sbt[sbt[t].l].size) lrotate(t);
else if (sbt[sbt[sbt[t].r].l].size>sbt[sbt[t].l].size) rrotate(sbt[t].r),lrotate(t);
else return ;
}
maintain(sbt[t].l,false); maintain(sbt[t].r,true);
maintain(t,true); maintain(t,false);
}
void ins(int& t,int& v)
{
if (t)
{
++sbt[t].size;
if (v<sbt[t].val) ins(sbt[t].l,v);
else ins(sbt[t].r,v);
maintain(t,v>=sbt[t].val);
}
else
{
++cnt; t=cnt;
sbt[t].val=v; sbt[t].size=1;
sbt[t].l=sbt[t].r=0;
}
}
int del(int& t,int v)
{
int ans=0; --sbt[t].size;
if ((v==sbt[t].val)||((v<sbt[t].val)&&!sbt[t].l)||((v>sbt[t].val)&&!sbt[t].r))
{
ans=sbt[t].val;
if (!sbt[t].l||!sbt[t].r) t=sbt[t].l+sbt[t].r;
else sbt[t].val=del(sbt[t].l,sbt[t].val+1);
return ans;
}
else
{
if (v<sbt[t].val) return del(sbt[t].l,v);
else return del(sbt[t].r,v);
}
}
bool find(int& t,int& v)
{
if (!t) return 0;
if (v<sbt[t].val) return find(sbt[t].l,v);
else return (sbt[t].val==v)||find(sbt[t].r,v);
}
int getval(int& t,int k) // recursion
{
if (k==sbt[sbt[t].l].size+1) return sbt[t].val;
if (k<=sbt[sbt[t].l].size) return getval(sbt[t].l,k);
else return getval(sbt[t].r,k-1-sbt[sbt[t].l].size);
}
int getrank(int& t,int& v) // recursion
{
if (!t) return 1;
if (v<=sbt[t].val) return getrank(sbt[t].l,v);
else return sbt[sbt[t].l].size+1+getrank(sbt[t].r,v);
}
public:
SBT(){cnt=root=0; sbt[0].size=0;}
inline void ins(int val){ins(root,val);}
inline void del(int val){del(root,val);}
inline int getval(int rk){return getval(root,rk);}
inline int getrank(int val){return getrank(root,val);}
inline int pre(int x){return getval(getrank(x)-1);}
inline int nxt(int x){return getval(getrank(x+1));}
}BST;
vector
using namespace std;
struct VectorTree
{
private:
vector<ll> v;
public:
#define LWBp lower_bound(v.begin(),v.end(),val) // pointer
#define LWB LWBp-v.begin() // value
inline void clear(){v.clear();}
VectorTree(){clear();}
inline void ins(ll val){v.insert(LWBp,val);}
inline void del(ll val){v.erase(LWBp,LWBp+1);}
inline int getrank(ll val){return LWB+1;}
inline ll getval(unsigned rk){return v[rk-1];}
inline ll pre(ll x){return getval(getrank(x)-1);}
inline ll nxt(ll x){return getval(getrank(x+1));} // suf
inline size_t size(){return v.size();}
#undef LWBp
#undef LWB
}BST;

文艺平衡树:

Splay

const int N = 3e5+500;
int n, m, val[N];
struct Splay
{
int root, fa[N], ch[N][2], siz[N], lazy[N], cc;
inline void pushup(int u){siz[u] = siz[ch[u][0]] + siz[ch[u][1]] + 1;}
inline bool son(int u){return u == ch[fa[u]][1];}
inline void rotate(int x)
{
int y = fa[x], z = fa[y], chk = son(x);
ch[z][son(y)] = x; fa[x] = z;
ch[y][chk] = ch[x][chk^1]; fa[ch[x][chk^1]] = y;
ch[x][chk^1] = y; fa[y] = x;
pushup(y); pushup(x);
}
inline void splay(int x, int to = 0)
{
while (fa[x] != to)
{
int y = fa[x];
if (fa[y] != to) rotate(son(y) == son(x) ? y : x);
rotate(x);
} if (!to) root = x;
}
inline void pushdown(int x)
{
if (lazy[x])
{
lazy[ch[x][0]] ^= 1; lazy[ch[x][1]] ^= 1;
swap(ch[x][0], ch[x][1]);
lazy[x] = 0;
}
}
inline void insert(int k)
{
int x = root, f = 0;
while (x && (k != val[x])){f = x; x = ch[x][k > val[x]];}
x = ++cc;
if (f) ch[f][k > val[f]] = x;
fa[x] = f; ch[x][0] = ch[x][1] = lazy[x] = 0;
siz[x] = 1; val[x] = k;
splay(x);
}
inline int kth(int k)
{
int x = root;
while (true)
{
pushdown(x);
if (k > siz[ch[x][0]] + 1){k -= siz[ch[x][0]] + 1; x = ch[x][1];}
else if (k <= siz[ch[x][0]]) x = ch[x][0];
else return x;
}
}
inline void reverse(int l, int r)
{
int p = kth(l-1), s = kth(r+1);
splay(p); splay(s, p);
lazy[ch[s][0]] ^= 1;
}
inline void ldr(int x)
{
if (!x) return ;
pushdown(x);
ldr(ch[x][0]);
if ((val[x] > 1) && (val[x] < n+2)) printf("%d ", val[x]-1);
ldr(ch[x][1]);
}
Splay(){root = cc = 0;}
}T;
// T.reverse(l+1, r+1)
fhq-Treap
const int N=1e5+15;
static mt19937 rnd(19260817);
struct revfhq
{
private:
struct Node{int l,r,val,size; bool reversed;}fhq[N];
int cnt,root;
inline int newnode(int val){fhq[++cnt].val=val; fhq[cnt].size=1; fhq[cnt].reversed=false; return cnt;}
inline void update(int now){fhq[now].size=fhq[fhq[now].l].size+fhq[fhq[now].r].size+1;}
inline void pushdown(int now)
{
swap(fhq[now].l,fhq[now].r);
fhq[fhq[now].l].reversed^=1; fhq[fhq[now].r].reversed^=1;
fhq[now].reversed=false;
}
void split(int now,int siz,int &x,int &y)
{
if (!now) x=y=0;
else
{
if (fhq[now].reversed) pushdown(now);
if (fhq[fhq[now].l].size<siz){x=now; split(fhq[now].r,siz-fhq[fhq[now].l].size-1,fhq[now].r,y);}
else{y=now; split(fhq[now].l,siz,x,fhq[now].l);}
update(now);
}
}
int merge(int x,int y)
{
if(!x||!y) return x+y;
if (int(rnd())%(fhq[x].size+fhq[x].size)<fhq[x].size)
{
if (fhq[x].reversed) pushdown(x);
fhq[x].r=merge(fhq[x].r,y); update(x); return x;
}
else
{
if (fhq[y].reversed) pushdown(y);
fhq[y].l=merge(x,fhq[y].l); update(y); return y;
}
return 0;
}
public:
inline void reverse(int l,int r)
{
static int x,y,z;
split(root,l-1,x,y); split(y,r-l+1,y,z);
fhq[y].reversed^=1; root=merge(merge(x,y),z);
}
inline void ldr(int now)
{
if (!now) return ;
if (fhq[now].reversed) pushdown(now);
ldr(fhq[now].l); printf("%d ",fhq[now].val); ldr(fhq[now].r);
}
inline void ldr(){ldr(root);}
inline void insLE(int i){root=merge(root,newnode(i));}
}BST;

二逼平衡树:

珂朵莉树 (ODT)

k-D Tree

动态树

持久化数据结构

动态规划

背包 DP

多项式

多项式乘法

计算几何

其他问题

高精度

正整数高精

整数高精

浮点数高精

见 此题 附件 .

浮点数高精 - plus

int128

偏序

三维偏序

CDQ 分治
using namespace std;
const int N=2e5+500;
typedef long long ll;
int n,m,k,cnt[N],ans[N];
struct Node{int a,b,c;}a[N];
struct Answer{int a,b,c,cnt,ans;}b[N];
template<typename T>
struct FenwickTree{/*这里应有一个树状数组*/};
FenwickTree<int> e;
bool cmp1(const Node& x,const Node& y)
{
if (x.a==y.a)
{
if (x.b==y.b) return x.c<y.c;
return x.b<y.b;
} return x.a<y.a;
}
bool cmp2(const Answer& x,const Answer& y)
{
if (x.b==y.b) return x.c<y.c;
return x.b<y.b;
}
void cdq(int l,int r)
{
if (l==r) return ;
int mid=(l+r)>>1;
cdq(l,mid); cdq(mid+1,r);
sort(b+l,b+mid+1,cmp2);
sort(b+mid+1,b+r+1,cmp2);
int j=l;
for (int i=mid+1;i<=r;i++)
{
while ((b[i].b>=b[j].b)&&(j<=mid)){e.add(b[j].c,b[j].cnt); ++j;}
b[i].ans+=e.query(b[i].c);
}
for (int i=l;i<j;i++) e.add(b[i].c,-b[i].cnt);
}
int main()
{
scanf("%d%d",&n,&k); e.resize(k);
for (int i=1;i<=n;i++) scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c);
sort(a+1,a+1+n,cmp1);
int cc=0;
for (int i=1;i<=n;i++)
{
++cc;
if ((a[i].a!=a[i+1].a)||(a[i].b!=a[i+1].b)||(a[i].c!=a[i+1].c))
{
++m;
b[m].a=a[i].a; b[m].b=a[i].b; b[m].c=a[i].c;
b[m].cnt=cc; cc=0;
}
}
cdq(1,m);
for (int i=1;i<=m;i++) ans[b[i].ans+b[i].cnt-1]+=b[i].cnt;
for (int i=0;i<n;i++) printf("%d\n",ans[i]);
return 0;
}
k-D Tree

bitset

悬线法

矩形面积并

自适应辛普森积分

自适应辛普森积分

牛顿迭代

牛迭

模拟退火 (SA)

SA

Fibonacci 数列

统一了线性递推的简单做法

递推

矩阵快速幂

扩域

斐波那契循环节

邪门算法

Dancing Links (DLX)

Berlekamp-Massey 算法 (BM)

动态图连通性

模板库 ~ Template library的相关教程结束。

《模板库 ~ Template library.doc》

下载本文的Word格式文档,以方便收藏与打印。