【备忘录】

· · 个人记录

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;
}

点分治

树的重心预备

树的重心代码

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

预备

代码

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); } ```