【备忘录】
zhzkiller
·
·
个人记录
Never Stop Upd
离散化
预备
a[n] 原数组。
b[m] 去重数组。
back[n] 备用数组。
代码
void discrete()
{
memcpy(back,a,sizeof(a));
sort(back+1,back+n+1);
for(int i=1;i<=n;i++) if(back[i]!=back[i-1]) b[++m]=back[i];
for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+m+1,a[i])-b;
}
并查集
预备
fa[x] 代表元素值。
代码
for(int i=1;i<=n;i++) fa[i]=i;
int find(int x)
{
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void merge(int x,int y)
{
fa[find(x)]=find(y);
}
莫队
树上莫队走欧拉序。
莫队预备
结构体 itv:
l 区间左端点。
r 区间右端点。
id 区间编号。
lca 区间端点的 LCA 节点(树上莫队专有)。
first[x] 树上节点 x 在欧拉序第一次出现点。
last[x] 树上节点 x 在欧拉序后一次出现点。
rev[x] 区间点 x 对应的树上节点。
莫队代码
inline bool cmp(interval a,interval b)
{
return ((a.l/t)^(b.l/t))?a.l<b.l:(((a.l/t)&1)?a.r<b.r:a.r>b.r);
}
void dfs(int x)
{
first[x]=++tot;
rev[tot]=x;
d[x]=d[f[x][0]]+1;
for(int i=h[x];i;i=edg[i].ne)
{
int y=edg[i].to;
if(y==f[x][0]) continue;
f[y][0]=x;
for(int j=1;j<=16;j++) f[y][j]=f[f[y][j-1]][j-1];
dfs(y);
}
last[x]=++tot;
rev[tot]=x;
}
点分治
树的重心预备
-
rt 当前节点。
-
fa 当前节点的父亲节点。
-
G 所求重心节点。
-
siz 当前子树大小。
-
size 已经覆盖子树大小。
-
sonsize 某一子树大小。
树的重心代码
int getG(int rt,int fa,int& G,int siz)
{
int size=1;
int g=0;
for(int i=h[rt];i;i=edg[i].ne)
{
int y=edg[i].to;
if(y==fa||vis[i]) continue;
int sonsize=getG(y,rt,G,siz);
size+=sonsize;
g=max(g,sonsize);
}
g=max(g,siz-size);
if(g<=siz/2) G=rt;
return size;
}
KMP
预备
-
ne[i] 下标为 i 的 next 指针。
-
f[i] 下标为 i 的匹配长度。
代码
for(int i=2,j=0;i<=n;i++)
{
while(j&&a[i]!=a[j+1]) j=ne[j];
if(a[i]==a[j+1]) j++;
ne[i]=j;
}
for(int i=1,j=0;i<=n;i++)
{
while(j&&(j==n||b[i]!=a[j+1])) j=ne[j];
if(b[i]==a[j+1]) j++;
f[i]=j;
//if(f[i]==n) ......
}
笛卡尔树
满足二叉搜索树和堆性质的二叉树,节点保存二元组 (k,w)。
性质
预备
$r$,右儿子节点
$fa$,父亲节点
$k$,节点下标
$w$,节点权值
* $a[i]$ 原数组。
### 代码
```cpp
void decarel()//小根堆
{
for(int i=1;i<=n;i++)
{
int k=i-1;t[i].w=a[i];
while(t[k].w>t[i].w) k=t[k].fa;
t[i].l=t[k].r;t[t[i].l].fa=i;
t[k].r=i;t[i].fa=k;
}
}
```
## 缩点
### SCC 缩点预备
* `dfn[x]` 节点 $x$ 的时间戳。
* `low[x]` 节点 $x$ 的追溯值。
* `st[top]` 当前强连通分量的节点集合。
* `inst[x]` 节点 $x$ 是否在当前强连通分量的节点集合中。
* `scc[num]` 第 $num$ 个强连通分量的节点集合。
* `c[x]` 节点 $x$ 属于强连通分量的编号。
### SCC 缩点代码
```cpp
void tarjan(int x)
{
dfn[x]=low[x]=++tot;
st[++top]=x;inst[x]=true;
for(int i=h[x];i;i=edg[i].ne)
{
int y=edg[i].to;
if(!dfn[y]) tarjan(y),low[x]=min(low[x],low[y]);
else if(inst[y]) low[x]=min(low[x],dfn[y]);
}
if(dfn[x]==low[x])
{
num++;int z;
do
{
z=st[top--],inst[z]=false;
c[z]=num;scc[num].push_back(z);
}
while(z!=x);
}
}
```
## 高斯线性消元
### 预备
* `a[n][n]` $n$ 个 $n$ 元一次方程组的系数。
* `ans[n]` $n$ 个未知数的解。
* `eps` 精度误差值,为 $10^{-7}$。
### 代码
```cpp
void ghose_division()
{
for(int i=1;i<=n;i++)
{
int mx=i;
for(int j=i+1;j<=n;j++) if(fabs(a[j][i])>fabs(a[mx][i])) mx=j;
if(fabs(a[mx][i])<eps)
{
flag=true;
return;
}
if(i!=mx) swap(a[i],a[mx]);
double tem=a[i][i];
for(int j=i;j<=n+1;j++) a[i][j]/=tem;
for(int j=i+1;j<=n;j++)
{
tem=a[j][i];
for(int k=i;k<=n+1;k++) a[j][k]-=a[i][k]*tem;
}
}
ans[n]=a[n][n+1];
for(int i=n-1;i;i--)
{
ans[i]=a[i][n+1];
for(int j=i+1;j<=n;j++) ans[i]-=a[i][j]*ans[j];
}
}
```
## 伯努利错排问题
$$f_1=0,f_2=1$$
$$f_i=(i-1)(f_{i-1}+f_{i-2}),n\ge3$$
## 二分
### 代码
* 在单调序列中查询目标中最小的一个:
```
int l=1,r=n;
while(l<r)
{
int mid=(l+r)>>1;
if(check(mid)) r=mid;
else l=mid+1;
}
```
* 在单调序列中查询目标中最大的一个
```cpp
int l=1,r=n;
while(l<r)
{
int mid=(l+r+1)>>1;
if(check(mid)) l=mid;
else r=mid-1;
}
```
* 通用版
```cpp
int l=1,r=n;
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) ans=mid,l=mid+1;
else r=mid-1;
}
```
## 树状数组
### 代码
```cpp
void add(int x,int y)
{
for(;x<=n;x+=x&-x) c[x]+=y;
}
int ask(int x)
{
int ans=0;
for(;x;x-=x&-x) ans+=c[x];
return ans;
}
```
## 质数与同余数学
有
$$a\times b=\gcd(a,b)\times\text{lcm}(a,b)$$
### 最大公因数公式
$$\forall a,b\in\mathbb N,\gcd(a,b)=\gcd(b,a-b)=\gcd(a,a-b)$$
$$\forall a,b\in\mathbb N,\gcd(2a,2b)=2\gcd(a,b)$$
欧几里得算法:
$$\forall a,b\in\mathbb N,b\ne0,\gcd(a,b)=\gcd(b,a\text{ mod }b)$$
Bezout 定理(扩展欧几里得算法):对任意整数 $a,b$,存在一对整数 $x,y$,使得
$$ax+by=\gcd(a,b)$$
### 最大公因数代码
```cpp
int gcd(int a,int b)
{
if(b==0) return a;
else return gcd(b,a%b);
}
int exgcd(int a,int b,int &x,int &y);
{
if(b==0)
{
x=1,y=0;
return a;
}
int d=exgcd(b,a%b,x,y);
int z=x;x=y;y=z-y*(a/b);
return d;
}
```
### 普通线性同余方程
求关于 $x$ 的同于方程 $ax\equiv1(\text{mod}\ b)$ 的最小正整数解。
```cpp
#include <bits/stdc++.h>
#define ll long long
using namespace std;
ll a,b,x,y;
ll exgcd(ll a,ll b,ll &x,ll &y)
{
if(b==0)
{
x=1,y=0;
return a;
}
ll d=exgcd(b,a%b,x,y);
ll z=x;x=y;y=z-y*(a/b);
return d;
}
```
最小正整数解即为 $(x\mod b+b) \mod b$。
### 逆元公式
$$\frac{a}{b}\equiv a\times inv_b\pmod{p}$$
当 $p$ 为质数时,
$$inv_x=x^{p-2}$$
### 逆元代码
```cpp
ll qpow(ll a,ll b,ll p)
{
ll res=1,base=a;
while(b)
{
if(b&1) res=res*base%p;
base=base*base%p;
b>>=1;
}
return res;
}
ll inv(ll x,ll p)
{
return qpow(x,p-2,p);
}
```
## 组合数学
### 公式
$$\sum_{l,l\in[0,k]}^n\mathrm{C}^k_l=\mathrm{C}^{k+1}_{n+1}$$
$$\sum_{l,l\in[0,k]}^n\dbinom{l}{k}=\dbinom{n+1}{k+1}$$
$$\mathrm{C}^m_n=\frac{n!}{m!(n-m)!}$$
$$\dbinom{n}{m}=\frac{n!}{m!(n-m)!}$$
卡特兰数:
$$Cat_n=\frac{\mathrm{C}^n_{2n}}{n+1}=\mathrm{C}^n_{2n}-\mathrm{C}^{n-1}_{2n}=\frac{Cat_{n-1}\times(4n-2)}{n+1}$$
$$Cat_0=0,Cat_1=1,Cat_n=\sum^{n-1}_{i=0}Cat_i\times Cat_{n-i-1},n\ge2$$
贝尔数:
$$B_0=1,B_n=\sum^{n-1}_{k=0}\mathrm{C}^k_{n-1}B_k,n\ge1$$
二项式定理:
$$(a+b)^n=\sum^n_{i=0}\mathrm{C}^i_na^ib^{n-i}$$
Lucas 定理:当 $p$ 为质数时,
$$\mathrm{C}^m_n\equiv\mathrm{C}^{m\mod p}_{n\mod p}\times\mathrm{C}^{\lfloor\frac{m}{p}\rfloor}_{\lfloor\frac{n}{p}\rfloor}\pmod{p}$$
### 预备
* `fac[n]` $n$ 的阶乘对 $p$ 取模
* `inv[n]` $n$ 的阶乘在模 $p$ 意义下的逆元
### 代码
```cpp
ll C(ll n,ll m,ll p)
{
if(m>n) return 0;
if(m<0||n<0) return 0;
if(m==0) return 1;
return fac[n]*inv[m]%p*inv[n-m]%p;
}
ll Lucas(ll n,ll m,ll p)
{
if(m>n) return 0;
if(m<0||n<0) return 0;
if(m==0) return 1;
return C(n%p,m%p,p)*Lucas(n/p,m/p,p)%p;
}
```
## 数链剖分
### 预备
* `fa[x]` 树上节点 $x$ 的父节点
* `siz[x]` 树上节点 $x$ 的子树大小
* `dep[x]` 树上节点 $x$ 的深度
* `top[x]` 树上节点 $x$ 所在重链的祖先
* `son[x]` 树上节点 $x$ 的重儿子
* `seg[x]` 树上节点 $x$ 对应在线段树上的节点编号
* `rev[x]` 线段树上节点 $x$ 对应在树上的节点编号
### 代码
```cpp
void dfs1(int x,int f)
{
siz[x]=1;
fa[x]=f;
dep[x]=dep[f]+1;
for(int i=h[x];i;i=edg[i].ne)
{
int y=edg[i].to;
if(y==f) continue;
dfs1(y,x);
siz[x]+=siz[y];
if(siz[y]>siz[son[x]]) son[x]=y;
}
}
void dfs2(int x,int f)
{
if(!f)
{
seg[x]=++tot;
rev[tot]=x;
top[x]=x;
}
if(son[x])
{
seg[son[x]]=++tot;
top[son[x]]=top[x];
rev[tot]=son[x];
dfs2(son[x],x);
}
for(int i=h[x];i;i=edg[i].ne)
{
int y=edg[i].to;
if(y==f||y==son[x]) continue;
seg[y]=++tot;
rev[tot]=y;
top[y]=y;
dfs2(y,x);
}
}
ll ask(int x,int y)
{
ll res=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
res=(res+query(1,seg[top[x]],seg[x]))%mod;
x=fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
res=(res+query(1,seg[y],seg[x]))%mod;
return res;
}
void renew(int x,int y,ll v)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]]) swap(x,y);
change(1,seg[top[x]],seg[x],v);
x=fa[top[x]];
}
if(dep[x]<dep[y]) swap(x,y);
change(1,seg[y],seg[x],v);
}
```