note-未完成

约定 _xxx() 表示有 xxx 功能的函数 且仅表意思 不代表具体函数

所有算法用伪代码描述

大概来说 所有的变量类型都是可以变动的 书写的仅是比较常用的类型

笔记部分口胡 不保证完全正确

大部分都是数学

图论

生成树

稠密图上 \(\mathrm{Prim}\) 优 稀疏图上 \(\mathrm{Kruskal}\) 优

Kruskal

复杂度 \(O(n \log n)\)

struct Edge{int from,to,dis;}e[MAX<<1]; //这里存图比较特殊
int f[MAX];
//
void addedge(int from,int to,int dis) {e[++cnt]=(Edge){from,to,dis};} //特殊的加边
//并查集
void union(int x,int y);
int find(int x);
//
int Kruskal()
{
    int re=0,tot=0;
    for(int i=1;i<=MAX;i++) f[i]=i;
    sort(e+1,e+num_edge+1,_cmpDis());
    for(int i=1;i<=num_edge;i++)
    {
    	int u=find(e[i].from),v=find(e[i].to);
        if(u==v) continue;
        tot+=1; re+=e[i].dis;
    	f[u]=v;
        if(tot==n-1) break;
    }
    return re;
}

Prim

复杂度 \(O(n \log n)\)

struct Edge{int next,to,dis;}e[N<<1];
int head[N],cnt,vis[N],dis[N];
_set(allVar,0);
//
void addege(int from,int to,int dis); //加边
//
int prim()
{
	int s=_rand(inV),tot=0,re=0,u=s;
    _set(dis,_minusINF()); dis[s]=0;
    for(int i=head[s];i;i=e[i].next) dis[e[i].to]=_cmp(dis[e[i].to],e[i].dis);
    while((++tot)<n)
    {
    	int m=_minusINF(); vis[u]=1;
        for(int i=1;i<=n;i++)
            if(!vis[i]&&_cmp(m,dis[i])) m=dis[i],u=i;
        re+=m;
        for(int i=head[u];i;i=e[i].next)
        {
        	int v=e[i].to;
            if(_cmp(dis[v],e[i].dis)&&!vis[v]) dis[v]=e[i].dis;
        }
    }
    return re;
}

重链剖分

建立复杂度 \(O(n)\)

int size[MAX],dep[MAX],fa[MAX],top[MAX],dfn[MAX],rnk[MAX],idx;
struct Edge{int next,to,dis;}e[MAX<<1];
_set(allVar,0);
//
void dfs1(int u,int f)
{
	size[u]=1; dep[u]=dep[f]+1; fa[u]=f;
    for(int i=head[u];i;i=e[i].next)
    {
    	int v=e[i].to;
        if(v==f) continue;
        dfs1(v,u);
        size[u]+=size[v];
        if(size[son[u]]<size[v]) son[u]=v;
    }
}
void dfs2(int u,int topf)
{
    top[u]=topf; dfn[u]=++idx; rnk[idx]=u;
    if(!son[u]) return ;
    dfs2(son[u],topf);
    for(int i=head[u];i;i=e[i].next)
    {
    	int v=e[i].to;
        if(v==fa[u]||v==son[u]) continue;
        dfs2(v,v);
    }
}

最近公共祖先(LCA)

树链剖分

单次查询复杂度 \(O(\log n)\)

跳链时每次跳深度大的点

int size[MAX],dep[MAX],fa[MAX],top[MAX],dfn[MAX],rnk[MAX],idx;
struct Edge{int next,to,dis;}e[MAX<<1];
_set(allVar,0);
//
void addedge(int from,int to,int dis); //加边
//重链剖分
void dfs1(int u,int f); 
void dfs2(int u,int topf);
//
int lca(int x,int y)
{
	while(top[x]!=top[y])
    {
    	if(dep[top[x]]>=dep[top[y]]) x=fa[top[x]];
        else y=fa[top[y]];
    }
    return dep[x]<dep[y]?x:y;
}

倍增

建立复杂度 \(O(n \log n)\)

单次查询复杂度 \(O(\log n)\)

int log2[MAX],dep[MAX],fa[MAX][log2[MAX]];
struct Edge{int next,to,dis;}e[MAX<<1];
//
void addedge(int from,int to,int dis); //加边
//开始前运行
getLog2();
perDfs(root,0);
//
void getLog2()
{
	log2[0]=-1;
    for(int i=1;i<=MAX;i++) log2[i]=log2[i>>1]+1;
}
void preDfs(int u,int f)
{
	dep[u]=dep[f]+1;
    fa[u][0]=f;
    for(int i=1;(1<<i)<=dep[u];i++) fa[u][i]=fa[fa[u][i-1]][i-1];
    for(int i=head[u];i;i=e[i].next)
		if(e[i].to!=f) preDfs(e[i].to,u);
}
int lca(int x,int y)
{
	if(dep[x]<dep[y]) swap(x,y);
    while(dep[x]>dep[y]) x=fa[x][log2[dep[x]-dep[y]]];
    if(x==y) return x;
    for(int i=log2[dep[x]];i;i--)
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}

最短路

Dijkstra

复杂度稳定的 \(O((v+e) \log v)\)

#define pii pair<int,int>
#define mp(X,Y) make_pair(X,Y)
//
int dis[MAX],vis[MAX];
struct Edge{int next,to,dis;}e[MAX<<1];
//
void addedge(int from,int to,int dis); //加边
//
void Dijkstra(int S,int T)
{
    _set(dis,INF); _set(vis,0);
    priority_queue<pii,vector<pii>,greater<pii> > q;
    q.push(mp(0,S)); dis[S]=0;
    while(!q.empty())
    {
    	int u=q.top().second; q.pop();
        if(vis[u]) continue;
        vis[u]=1;
        for(int i=head[u];i;i=e[i].next)
        {
        	int v=e[i].to;
            if(dis[v]>dis[u]+e[i].dis)
            {
            	dis[v]=dis[u]+e[i].dis;
                if(!vis[v]) q.push(mp(dis[v],v));
            }
        }
    }
    return dis[T];
}

SPFA

大部分复杂度 \(O(kn)\) 最坏 \(O(nm)\)

在分层图上跑的飞飞飞飞快

int dis[MAX],vis[MAX];
struct Edge{int next,to,dis;}e[MAX<<1];
//
void addedge(int from,int to,int dis); //加边
//
void SPFA(int S,int T)
{
    _set(dis,INF); _set(vis,0);
    queue<int> q;
    q.push(S); dis[S]=0; vis[S]=1;
    while(!q.empty())
    {
    	int u=q.front(); q.pop();
        vis[u]=0;
        for(int i=head[u];i;i=e[i].next)
        {
        	int v=e[i].to;
            if(dis[v]>dis[u]+e[i].dis)
            {
            	dis[v]=dis[u]+e[i].dis;
                if(vis[v]==0) vis[v]=1,q.push(v);
            }
        }
    }
}

Floyd

复杂度 \(O(n^3)\)

int e[MAX][MAX];
//
_set(e,INF);
_set(e[u][v],min(dis[u][v]));
void Floyd()
{
	for(int k=1;k<=MAX;k++)
		for(int i=1;i<=MAX;i++)
            for(int j=1;j<=MAX;j++) e[i][j]=min(e[i][j],e[i][k]+e[k][j]);
}

Johnson

全源最短路 复杂度 \(O(nm \log m)\)

新建一个超级源点 \(S\) ,从其向所有点连一条边权为 \(0\) 的边

用队列优化的 \(Bellman-Ford\) 算法求出 \(0\) 号点到其它所有点的最短路

对于一条 \(u \to v\) 边权为 \(w\) 的边,将边权重新设为 \(w+h_i-h_v\)

再以每个点为起点 求出任意两点之间的最短路

int dis[MAX],vis[MAX],h[MAX];
struct Edge{int next,to,dis;}e[MAX<<1];
//
void addedge(int from,int to,int dis);
bool SPFA(int S); //普通的SPFA+判负环 true/false表是否有负环
void Dijkstra(int S);
//
void Johnson()
{
	_getGap();
    int S=_nonGapV(); //S=0
    for(int i=1;i<=MAX;i++) addedge(S,i,0);
    if(SPFA(S)) //SPFA跑出来的最短路记为 h[MAX]
    {
    	_getNegativeCircle();
        return ;
    }
	for(int u=1;u<=n;u++)
       	for(int i=head[u];i;i=e[i].next)
        {
        	int v=e[i].to;
            e[i].w=h[u]-h[v];
        }
    for(int i=1;i<=n;i++) Dijkstra(i);为
}

网络流

Dinic

数据结构

并查集

如果加了按秩合并+路径压缩 单次修改 查询 复杂度 \(O(\alpha(n))\)

只有路径压缩 单次修改 查询 复杂度 \(O( \log n)\)

按秩合并策略:将小的树连到大的树上

int f[MAX],size[MAX];
_set(f[i],i); _set(size[i],0)
//
void union(int x,int y) //按秩合并
{
	int fx=query(x),fy=query(y);
    if(fx==fy) return ;
    if(size[fx]>size[fy]) swap(fx,fy);
    f[fx]=fy; size[fy]+=size[fx];
}
int query(int x) //路径压缩
{
	return f[x]==x?x:f[x]=query(f[x]);
}

STL

priority_queue<T,vector<T>,greater<T> > ;
priority_queue<T,vector<T>,less<T> > ;

手写

struct Heap{
	int h[MAX],size;
    void init() {_set(h,size,0);} 
	void push(int v)
    {
        int now=v;
    	h[++size]=v;
    	while(now)
        {
        	int next=now>>1;
            if(_cmp(h[next],h[now])) swap(h[next],h[now]);
            else break;
            now=next;
        }
    }
    void top() {return h[1];}
    void pop()
    {
    	swap(h[1],h[size--]);
        int now=1;
        while((now<<1)<=size)
        {
        	int next=now<<1;
            if(next+1<=size&&_cmp(h[next+1],h[next])) next++;
            if(_cmp(h[next,h[now]])) swap(h[next],h[now]);
            else break;
            now=next;
        }
    }
};

树状数组

单次修改查询复杂度 \(O(\log n)\)

常数小 跑得比线段数快

单修区查

struct BitTree{
	int t[MAX],n;
    void init() {_set(t,_need());}
    int lowbit(int x) {return x&(-x)};
    void modify(int x,int k) {while(x<=n) t[x]=_update(t[x],k),x+=lowbit(x);}
	int query(int x)
    {
    	int re=0;
        while(x) re=_update(re,t[x]),x-=lowbit(x);
        return re;
    }
};

区修单查

struct BitTree{
	int s[MAX],delta[MAX],n;
    void init() {_set(delta,_need()); _set(s);}
    void modify(int x,int k) {while(x<=n) delta[x]=_update(delta[x],k),x+=lowbit(x);}
    void modify(int l,int r,int k) {modify(l,k); modify(r+1,_invOperation(k));}
    int query(int x)
    {
    	return re=0;
        while(x) re=_update(re,delta[x]),x-=lowbit(x);
        return _update(re,s[x]);
    }
};

线段树

单次修改 查询复杂度 \(O(\log n)\)

struct Sgtree{
    int init[MAX];
    struct node{int l,r,...;}t[MAX<<2];
    void init() {_set(init);}
    void update(int p) {_update_from_ltree_rtree();}
    void pushdown(int p) {_pushdown_to_ltree__rtree();}
    void build(int p,int l,int r)
    {
        t[p].l=l; t[p].r=r; _setnode();
    	if(l==r)
        {
            t[p]=_newnode(p);
        	return ;
        }
        int mid=(l+r)>>1;
        build(p<<1,l,mid); build(p<<1|1,mid+1,r);
        update(p);
    }
    void modify(int p,int l,int r,int k)
    {
    	if(l<=t[p].l&&r>=t[p].r)
        {
        	_modifynode();
            return ;
        }
        pushdown(p);
        int mid=(t[p].r+t[p].l)>>1;
        if(l<=mid) modify(p<<1,l,mid);
        if(r>mid) modify(p<<1|1,mid+1,r);
        update(p);
    }
    int query(int p,int l,int r)
    {
    	if(l>=t[p].l&&r>=t[p].r) return _needVal();
        pushdown(p);
        int mid=(t[p].l+t[p].r)>>1,re=0;
        if(l<=mid) re=_update(re,query(p<<1,l,r));
        if(r>mid) re=_update(re,query(p<<1|1,l,r));
        return re;
    }
};

ST表

\(O(n \log n)\) 建表 \(O(1)\) 查询

int log2[MAX],st[MAX][log2[MAX]];
//
void preSolve()
{
	log2[0]=-1;
    for(int i=1;i<=MAX;i++) log2[i]=log2[i>>1]+1;
    for(int i=1;i<=MAX;i++) _get(st[i][0]); //每个点
	for(int j=1;j<=log2[MAX];j++)
        for(int i=1;i+(1<<j)-1<=n;i++)
            st[i][j]=_cmp(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
void RMQ(int l,int r)
{
	int t=log2[r-l+1];
    return max(st[l][t],st[r-(1<<t)+1][t]);
}

平衡树

Fhq-Treap

Splay

珂朵莉树

仅在数据随机的情况下 复杂度为 \(O(n \log n)\)

大部分情况都是 \(O(玄学)\)

#define iter set<node>::iterator
//
struct node{
	int l,r; mutable int v;
    node(const int L,int R=-1,int V=0):l(L),r(R),v(V){}
    bool operator<(const node &x)const {return l<x.l;}
};
set<node> s;
//
iter split(int p)
{
	if(p>MAX) return s.end();
    iter i=--s.upper_bound(node(p));
    if(i->l==p) return i;
    int l=i->l,r=i->r,v=i->v;
    s.erase(i);
    return s.insert(node(l,p-1,v)),s.insert(node(p,r,v)).first;
}
void assign(int l,int r,int v)
{
	iter ir=split(r+1),il=split(l);
    s.erase(il,ir);
    s.insert(node(l,r,v));
}
int performance(int l,int r)
{
	iter ir=split(r+1),il=split(l);
    for(;il!=ir;++il)
    	_anything();
}

分块

一般的 取块大小为 \(\sqrt n\)

int s[MAX],block[SIZE];
//
void build()
{
    _get(s);
	int size=_size(MAX);
    for(int i=1;i<=n;i++)
    {
    	id[i]=(i-1)/size+1;
        _update(block[id[i]],s[i]);
    }
}
int action(int l,int r,...)
{
	int lid=id[l],rid=id[r];
    if(lid==rid)
    {
    	for(int i=l;i<=r;i++) _solve(s[i],block[i]);
    	return _need();
    }
    for(int i=l;id[i]==lid;i++) _solve(s[i],block[i]);
    for(int i=lid+1;i<rid;i++) _solve(block[i]*size);
    for(int i=r;id[i]==rid;i--) _solve(s[i],block[i]);
    return _need();
}

数学

数学部分的代码部分还在咕咕中

快速幂

求解 \(a^k\equiv c \mod p\) 中的 \(c\)

int qpow(int a,int k,int p) //a^k (mod p)
{
    int re=1;
	while(k)
    {
    	if(k&1) re*=a,re%=p;
        a*=a; a%=p;
        k>>=1;
    }
    return re;
}
//另一种写法
int qpow(int a,int b,int p)
{
	int re=1;
    for(;b;b>>=1,a=a*a%p)
        if(b&1) re=re*a%p;
    return re;
}

质数

素数分布定理

设 \(\pi(x)\) 是不超过 \(x\) 的素数的个数 则 \(\pi(x) \sim \dfrac{x}{\ln x}\)

算术基本定理

任何一个大于 \(1\) 的正整数都能有限分解为有限个质数的乘积 即 \(N=p_1^{c_1}p_2^{c_2}...p_m^{c_m}\)

其中 \(c_i\) 为正整数 \(p_i\) 为质数 且满足 \(p_1<p_2<...<p_m\)

算术基本定理的推论

有 \(N\) 的正约束集合 \((p_1^{b_1}p_2^{b_2}...p_m^{b_m})\) 其中 \(0\le b_i\le c_i\)

有 \(N\) 的正约数个数 \((c_1+1)\times (c_2+1)\times ... \times (c_m+1)=\prod\limits_{i=1}^{m}(c_i+1)\)

有 \(N\) 的所有正约数的和 \((1+p_1+...+p_1^{c_1})\times ... \times (1+p_m+...+p_m^{c_m})=\prod\limits_{i=1}^{m}(\sum\limits_{j=0}^{c_i}(p_i)^j)\)

线性筛

int noPrime[i];
//
void getPrime()
{
    _set(noPrime,0);
	noPrime[0]=noPrime[1]=1;
    for(int i=2;i*i<=MAX;i++)
    	if(!noPrime[i]) for(int j=i+i;j<=MAX;j+=i) noPrime[j]=1;
}

同余

费马小定理

若 \(p\) 为质数 则对于任意整数 \(a\) 有 \(a^p \equiv a\mod p\)

欧拉定理

若 \(a,m\) 互质 则 \(a^{\varphi(m)}\equiv 1 \mod m\)

扩展欧拉定理

\[a^b \equiv \left\{\begin{aligned} a^{b \mod \varphi(p)} \qquad gcd(a,p)=1 \\ a^b \qquad gcd(a,p)\ne1,b<\varphi(p)\\ a^{b\mod \varphi(p)+\varphi(p)} \qquad gcd(a,p)\ne1,b\ge \varphi(p) \end{aligned} \right. \mod p \]

逆元

线性求逆元

要求 \(p\) 为质数

int inv[MAX];
//
void getInv()
{
    inv[1]=1;
	for(int i=2;i<=n;i++) inv[i]=(p-(p/i))*inv[p%i]%p;
}

拓展欧几里得求逆元

即求 \(ax+py\equiv1 \mod p\) 的一组整数解

要求 \(gcd(a,p)=1\)

则 \(x\) 为一个解 所以 \(a^{-1}=x\)

exgcd(a,p,x,y);

费马小定理求逆元

有 \(a^{p-1}\equiv 1 \mod p\) 要求 \(p\) 为质数

所以 \(a^{-1}=a^{p-2}\)

invA=qpow(a,p-2,p);

最大公约数

最大公约数定理

有 \(\forall a,b \in \N ,\qquad gcd(a,b)\times lcm(a,b)=a\times b\)

有时 也记 \((a,b)\) 为 \(gcd(a,b)\) 记 \([a,b]\) 为 \(lcm(a,b)\)

有 \((a,b)[a,b]=ab\)

辗转相除法

求 \(gcd(x,y)=c\) 的 \(c\)

int gcd(int x,int y)
{
	return y==0?x:gcd(y,x%y);
}

位运算加速(有一定限制 找不到了 一般都没问题)

int gcd(int x,int y)
{
	while(y^=x^=y^=x%=y) ;
    return x;
}

拓展欧几里德算法

给出不定方程 \(ax+by=gcd(a,b)\) 求出其一组特解

void exgcd(int a,int b,int &x,int &y)
{
	if(b==0) x=1,y=0;
    else
    {
    	exgcd(b,a%b,x,y);
        int d=x;
        x=y; y=d-a/b*y;
    }
}

积性函数

若当 \(a,b\) 互质 有 \(f(ab)=f(a)f(b)\) 则 \(f\) 为积性函数

欧拉函数

欧拉函数

定义 \(\varphi(n)\) 即小于等于 \(n\) 的与 \(n\) 互相质的数的个数

性质:

  • \(\varphi(1)=1\)
  • 当 \(n\) 为质数 \(\varphi(n)=n-1\)
  • \(\varphi(n)\) 是积性函数 则若 \(a,b\) 互质 \(\varphi(ab)= \varphi(a)\varphi(b)\)
  • \(\forall n>1\) \(1\sim n\) 中与 \(n\) 互质的数的和为 \(\frac{n\times \varphi(n)}{2}\)

对于一个数 \(n\) 有 \(\varphi(n)=n\times \frac{p_1-1}{p_1}\times ...\times \frac{p_m-1}{p_m}=n\times \prod\limits_{p|n}(1-\frac{1}{p})\) 其中 \(p\) 为质数

筛法

有 \(\varphi(n)=\varphi(p)\times\varphi(n')=(p-1)\times\varphi(n')\)

int phi[MAX];
//
void getPhi()
{
	_set(phi,0);
    phi[1]=1;
    for(int i=2;i<=n;i++)
        if(!phi[i])
            for(int j=i;j<=n;j+=i)
            {
            	if(!phi[j]) phi[j]=j;
                phi[j]=phi[j]/i*(i-1);
            }
}

线性基

给出一个集合 \(A\) , 令 \(c=c\ xor \ x_i \quad x_i \in A\) , 求最大的 \(c\)

long long A[MAX],bit[65],ans;
//
void getBit(int x)
{
	for(int i=62;i;i--)
    {
    	if(!(x>>(ll)i)) continue;
        if(!bit[i])
        {
            bit[i]=x;
            break;
        }
        x^=bit[i];
    }
}
int solve()
{
	for(int i=1;i<=MAX;i++) _get(A);
    for(int i=1;i<=MAX;i++) getBit(A[i]);
	ans=0;
    for(int i=62;i;i--)
        if((ans^bit[i])>ans) ans^=bit[i];
    return ans;
}

同余方程

线性同余方程

给出 \(a,b,m\) 求一个整数 \(x\) 满足 \(ax \equiv b \mod m\)

原式可转为 \(ax+my=b\) 则有

\(ax+my=b \iff a\frac{x_0b}{(a,m)}+b\frac{y_0b}{(a,m)}=b\) 当且仅当 \((a,m)|b\) 有整数解

则有一组特解 \(x_0=x'\times \frac{b}{(a,m)},y_0=y'\times \frac{b}{(a,m)}\)

有通解 \(x=x_0+k\frac{m}{(a,m)},y=y_0+k\frac{a}{(a,m)} \quad k\in \Z\)

中国剩余定理

设 \(m_1,m_2,...,m_n\) 是两两互质的整数 令 \(m=\prod\limits_{i=1}^{n} m_i,M_i=m/m_i\) \(t_i\) 是方程 \(M_it_i \equiv 1 \mod m_i\) 的一个解

则 对于任意的 \(n\) 个整数 \(a_1,a_2,...,a_n\) 方程组

\[\left\{\begin{aligned} x \equiv a_1 \mod m_1 \\ x \equiv a_2 \mod m_2 \\ \vdots \\ x \equiv a_n \mod m_n \end{aligned} \right. \]

有整数解 为 \(x=\sum\limits_{i=1}^{n} a_iM_it_i\)

拓展中国剩余定理

给出数列 \(a_1,a_2,...,a_n\) 和 数列 \(m_1,m_2,...,m_n\) 求一个最小正整数 \(x\) 满足 \(\forall i\in[i,n]\) 有 \(x\equiv a_i \mod m_i\)

或者判断无解 与上不同的是 \(m_i\) 不一定两两互质

考虑数学归纳法

假设已经求出了前 \(k-1\) 个方程构成的方程组的解 \(x\) 记 \(m=\prod\limits_{i=1}^{k-1} m_i\) 则 \(x+qm,q\in \Z\) 是方程的通解

考虑加入第 \(k\) 个方程 现要求求出一个整数 \(t\) 使 \(x+tm \equiv a_k \mod m_k\)

该方程等价于 求解 \(tm \equiv a_k-x \mod k\) 的解 \(t\)

此时可求出解或判断有无解

新方程组的解即为 \(x'=x+tm\)

则反复使用 \(n\) 次拓展欧几里得算法即可求解整个方程组

BSGS

给出 \(b,p,n\) 求 形如 \(b^l =n \mod p\) 时 \(l\) 的值

#define ll long long
//
ll qpow(ll a,ll k,ll p); //快速幂
//
ll BSGS(ll p,ll b,ll n)
{
    map<ll,ll> h;
    ll now=n%p;
	ll m=ceil(sqrt(p));
    for(int i=1;i<=m;i++)
    {
    	now*=b; now%=p;
        if(!h[now]) h[now]=i;
    }
    now=qpow(b,m,p);
    ll tmp=now;
    for(int i=1;i<=m;i++)
        if(h[now]) return (i*m-h[now]%p+p)%p;
    	else now*=tmp,now%=p;
    return -1;
}

矩阵

由 \(n\times m\) 个数 \(a_{i,j}\) 排成的 \(m\) 行 \(n\) 列的数表

\[\notag \begin{pmatrix} a_{11} & a_{12} & \cdots & a_{1n} \\ a_{21} & a_{22} & \cdots & a_{2n} \\ \vdots & \vdots & & \vdots \\ a_{m_1} & a_{m2} & \cdots & a_{mn} \end{pmatrix}_{m \times n} \quad \]

注:矩阵本身并不代表任何意义,甚至可以看作一个二维数组,但是,可以人为赋予意义或从多个角度看待矩阵

可以说 矩阵是描述抽象事物的工具

特别的 当 \(n=m\) 时 可以称为方阵 当 \(n=1\) 或 \(m=1\) 时 可以看作向量

如果一个矩阵的元素可以被一个只与行 \(i\) 列 \(j\) 有关的统一函数 \(f\) 表示 也可记为 \((f(i,j))_{m \times n}\)

运算

一个 \(n\times m\) 的矩阵可以看作 \(n\times m\) 的二维数组

有三个矩阵 \(A,B,C\) 有

加减法

\(C=A \pm B \iff \forall i\in [1,n],\forall j\in[1,m],C_{i,j}=A_{i,j} \pm B_{i,j}\)

即有

\[\notag \begin{pmatrix} a_{11} & \cdots & a_{1n} \\ \vdots & & \vdots \\ a_{m1} & \cdots & a_{mn} \end{pmatrix} \quad \pm \begin{pmatrix} b_{11} & \cdots & b_{1n} \\ \vdots & & \vdots \\ b_{m1} & \cdots & b_{mn} \end{pmatrix} \quad = \begin{pmatrix} a_{11} \pm b_{11} & \cdots & a_{1n}\pm b_{1n} \\ \vdots & & \vdots \\ a_{m1} \pm b_{m1} & \cdots & a_{mn} \pm b{mn} \end{pmatrix} \quad \]

乘法

数乘

\(\lambda A \iff \forall i\in [1,n],\forall j\in[1,m],A_{i,j}=\lambda A_{i,j}\)

即有

\[\notag \lambda \begin{pmatrix} a_{11} & \cdots & a_{1n} \\ \vdots & & \vdots \\ a_{m1} & \cdots & a_{mn} \end{pmatrix} \quad = \begin{pmatrix} \lambda a_{11} & \cdots & \lambda a_{1n} \\ \vdots & & \vdots \\ \lambda a_{m1} & \cdots & \lambda a_{mn} \end{pmatrix} \quad \]

矩阵相乘

设 \(A\) 是 \(n \times m\) 的矩阵 \(B\) 为 \(m \times p\) 的矩阵

则 \(C=AB\) 是 \(n\times p\) 的矩阵 仅有 \(A\) 的列数(行数)与 \(B\) 的行数(列数)相等的时候才能求

\(C=A B \iff \forall i\in [1,n],\forall j\in[1,p],C_{i,j}=\sum\limits_{k=1}^{m} (A_{i,k}\times B_{k,j})\)

即 有 \(m \times p\) 的矩阵 \(A\) 设其一行为 \(A_i\) 有 \(p \times n\) 的矩阵 \(B\) 设其一列为 \(B_i\) 则有

\(m \times n\) 的矩阵 \(C=AB\)

\[\notag \begin{pmatrix} A_1 \\ A_2 \\ \vdots \\ A_m \end{pmatrix} \quad \begin{pmatrix} B_1,B_2,\cdots,B_n \end{pmatrix} \quad = \begin{pmatrix} A_1B_1 & \cdots & A_1B_n\\ \vdots & & \vdots \\ A_mB_1 & \cdots & A_mB_n \end{pmatrix} \quad \]

其中 \(A_iB_j=\sum\limits_{k=1}^{p} a_{k}b_{k}矩阵\)

这里用了矩阵分块法....矩阵是可以划分成子矩阵形成的矩阵的

性质

加减
  • \(A+B=B+A\)
  • \((A+B)+C=A+(B+C)\)
  • \(A+(-A)=O\)
  • \(A-B=A+(-B)\)
数乘
  • \((\lambda \mu) A = \lambda (\mu A)\)
  • \(\lambda A+\lambda B=\lambda(A+B)\)
  • \(\lambda(A+B)=\lambda A+\lambda B\)
  • \((AB)C=A(BC)\)
  • \(\lambda (AB)=(\lambda A)B=A (\lambda B)\) 其中 \(\lambda\) 为数
  • \(A(B+C)=AB+AC,(B+C)A=BA+CA\)
  • \(EA=AE=A\)
  • \(A^kA^l=A^{k+l}\) \((A^k)^l=A^{kl}\)

矩阵乘法不满足交换律 有 \(AB\) 不一定等于 \(BA\)

转置矩阵

对于一个矩阵 \(A\) 交换其行与列,记为 \(A^T\)

即有

\[\notag \begin{pmatrix} a_1 \\ a_2 \\ \vdots \\ a_m \end{pmatrix} ^T = \quad \begin{pmatrix} a_1,a_2,\cdots,a_n \end{pmatrix} \quad \]

性质

  • \((A^T)^T=A\)
  • \((A+B)^T=A^T+B^T\)
  • \((\lambda A)^T=\lambda A^T\)
  • \((AB)^T=A^TB^T\)

单位矩阵

主对角线为 \(1\) 其余为 \(0\) 的矩阵 是一个 \(n\times n\) 的方阵

可记为 \(I\) \(E\) \(U\)

有 \(I_n=\mathrm{diag}(1,1,...,1)\)

余子式

设一个 \(n \times m\) 的矩阵 \(A\)

则 \(A\) 的 \((i,j)\) 余子式 \(M_{ij}\) 为 \(A\) 去掉第 \(i\) 行 第 \(j\) 列后得到的 \(n-1\) 阶子矩阵的行列式

代数余子式

一个矩阵 \(A\) 的代数余子式 \(C_{ij}=(-1)^{i+j}M_{ij}\)

伴随矩阵

将行列式 \(|A|\) 的各元素的代数余子式 \(A_{ij}\) 构成以下矩阵

\[\notag A^* = \begin{pmatrix} A_{11} & \cdots & A_{1n} \\ \vdots & & \vdots \\ A_{m1} & \cdots & A_{mn} \end{pmatrix} \quad \]

则 \(A^*\) 为 \(A\) 的伴随矩阵

有 \(AA*=A^*A=|A|E\)

逆矩阵

对于 \(n\) 阶矩阵 \(A\) 如果有一个 \(n\) 阶矩阵 \(B\) ,使 \(AB=BA=E\)

那么称矩阵 \(A\) 是可逆的,称 \(B\) 为 \(A\) 的逆矩阵,记为 \(A^{-1}\)

性质

  • 若 \(A\) 可逆,则其逆矩阵唯一
  • 若 \(A\) 可逆,则 \(|A| \ne 0\)
  • 若 \(|A|\ne 0\) 则矩阵 \(A\) 可逆,且 \(A^{-1}=\dfrac{1}{|A|}A^*\)
  • 若 \(AB=E\) 或 \(BA=E\) 则 \(B=A^{-1}\)

运算规律

  • 若 \(A\) 可逆,则 \(A^{-1}\) 也可逆,且 \((A^{-1})^{-1}=A\)
  • 若 \(A\) 可逆,数 \(\lambda \ne 0\) ,则 \(\lambda A\) 可逆,且 \((\lambda A)^{-1}=\dfrac{1}{\lambda} A^{-1}\)
  • 若 $A,B $ 为同阶矩阵且均可逆,则 \(AB\) 也可逆,且 \((AB)^{-1}=B^{-1}A^{-1}\)

线性方程组

线性方程组为以下形式

\[\left\{\begin{aligned} a_{11}x_1+a_{12}x_2+\cdots +a_{1n}x_n=b_1 \\ \vdots \\ a_{m1}x_1+a_{m2}x_2+\cdots +a_{mn}x_n=b_m \end{aligned} \right. \]

可记为一个向量方程 \(Ax=b\)

其中 有

\[\notag A= \begin{pmatrix} a_{11} & \cdots & a_{1n} \\ \vdots && \vdots \\ a_{m1} & \cdots & a_{mn} \end{pmatrix} \quad , x=\begin{pmatrix} x_1 \\ \vdots \\ x_n \end{pmatrix} \quad , b= \begin{pmatrix} b_1 \\ \vdots \\ b_n \end{pmatrix} \quad \]

线性变换

对于一个变换 \(A\) 取两个向量,满足可加性齐次性

\[\notag A(\alpha+\beta)=A \alpha+A \beta \\ A(k \alpha) =k A(\alpha) \]

则可以用矩阵描述这种变换

亦即 \(f(x)=A_fx\)

为 \(\R^n\) 射到 \(\R^m\) 的线性变换构成的向量空间 \(\mathcal{L}(\R^n,\R^m)\) 上存在的一个到 \(\mathcal{M}(m,n,\R)\) 的一一映射: \(f \mapsto A_f\)

图论

可以用一个矩阵 \(E\) 描述一个有限图, \(E\) 为相关矩阵的邻接矩阵,记录了图两顶点之间是否有边链接.对一个简单图而言,其元素取值为 \(0/1\)

一个距离矩阵 \(E\) 各元素为点之间距离的矩阵

构造(转移)矩阵

考虑一种特殊的情况

设 \(F\) 是一个 \(1\times n\) 的矩阵(也就是数列) \(A\) 是 \(n \times n\) 的(转移)矩阵

有 \(F,F'\) 为一维数组 略去其行下标

对于 \(\forall j \in [1,n]\) 有 \(F'_j=\sum\limits_{k=1}^{n}(F_k \times A_{k,j})\)

等价于在一个向量的 \(k,j\) 两个状态之间发生了转移

这样 可以用 状态矩阵 \(F\) 与一个 转移矩阵 \(A\) 来表示数列的线性递推关系

同时 可以用矩阵快速幂优化这个过程 总复杂度 \(O(n^3 \log T)\)

OI会用到的

矩阵加速递推

矩阵表达修改

定长路径统计

定长最短路

限长路径计数/最短路

struct Matrix //这份矩阵的正确性无法保证
{
    int e[MAX][MAX],n,m;
    Matrix E(int n);
    Matrix(int N=0,int M=0):n(N),m(M),e(){}
    Matrix operator+(const Matrix &x)const ;
    Matrix operator*(const Matrix &x)const ;
};

Matrix Matrix::I(int n)
{
    Matrix t=Matrix(n,n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++) t.e[i][j]=1;
    return t;
}
Matrix Matrix::operator*(const Matrix &x)const
{
    Matrix t=Matrix(n,x.m);
    for(int k=1;k<=m;k++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=x.m;j++) t.e[i][j]+=e[i][k]*x.e[k][j];
    return t;
}
Matrix Matrix::operator+(const Matrix &x)const
{
    Matrix t=Matrix(n,m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++) t.e[i][j]=e[i][j]+x.e[i][j];
    return t;
}
Matrix qpow(Matrix x,int k)
{
    Matrix e=e.I(x.n),t(x);
    while(k)
    {
        if(k&1) e=e*t;
        t=t*t; k>>=1;
    }
    return e;
}

行列式

在数学中,行列式是一个函数,其定义域为 \(det\) 的矩阵 \(A\),取值为一个标量,写作 \(det(A)\) 或 \(|A|\)

逆序

对于 \(n\) 个不同元素,有一个标准次序 \(1,2,...,n\) 其下标为 \(p_1,p_2,...,p_n\)

一个逆序对 即 \(p_i>p_j\) 且 \(i<j\) 的有序数对

一个序列的逆序即其逆序对总数

计算

设有 \(n^2\) 个数,排成 \(n \times n\) 的数表

\[\notag D= \begin{vmatrix} a_{11} & \cdots & a_{1n} \\ \vdots & & \vdots \\ a_{n1} & \cdots & a_{nn} \end{vmatrix} \quad \]

令 \(t\) 为列下标排列 \(p_1p_2...p_n\) 的逆序数

则 \(D=\sum (-1)^t a_{1 p_1} a_{2p_2}\cdots a_{n p_n}\)

性质

...直接爆算

意义

从几何上来看 行列式 \(R^n\) 是 \(n\) 维空间下几何的体积

组合计数

加法原理

若完成一件事的方法有 \(n\) 类,其中第 \(i\) 类方法包括 \(a_i\) 种不同的方法,且这些方法互不重和

则完成这件事共有 \(a_1+a_2+...+a_n\) 种不同的方法

乘法原理

若完成一件事需要 \(n\) 个步骤,其中第 \(i\) 步骤有 \(a_i\) 种不同的完成方法,且这些步骤互不干扰

则完成这件事共有 \(a_1\times a_2 \times ... \times a_n\) 种不同的方法

排列数

从 \(n\) 个不同元素一次取出 \(m\) 个元素排成一列,产生的不同排列的数量(记为 \(P\) )

则 \(P_n^m=\dfrac{n!}{(n-m)!}\)

多重集合的排列数

设 \(S=\{n_1\cdot a_1,n_2 \cdot a_2 ,...,n_k \cdot a_k \}\) 是一个多重集,则 \(S\) 的全排列个数为 \(\dfrac{n!}{n_1!n_2!...n_k!}\)

多重集合的组合数

设 \(S=\{n_1\cdot a_1,n_2 \cdot a_2 ,...,n_k \cdot a_k \}\) 是一个多重集,则从 \(S\) 中取 \(r\) 个组成的多重集(不考虑元素顺序)数量为 \(C_{n+r-1}^{k-1}\)

组合数

从 \(n\) 个不同元素依次取出 \(m\) 个元素组成与一个集合(不考虑顺序),产生的不同集合的数量(记为 \(C\) )

则 \(C_n^m=\dfrac{n!}{m!(n-m)!}\)

性质

  • \(C_n^m=C_n^{n-m}\)

  • \(C_n^m=C_{n-1}^m+C_{n-1}^{m-1}\)

  • \(C_n^0+C_n^1+...+CN-^n=2^n\)

二项式定理

有 \((a+b)^n=\sum\limits_{k=0}^n C_n^ka^kb^{n-k}\)

Lucas 定理

若 \(p\) 是质数,则对于任意的 \(1\le m \le n\) 有

\[\notag C_n^m=C^{m \mod p}_{n \mod p} \times C^{m/p}_{n/p} \]

int C(int n,int m); //计算组合数
//
int Lucas(int n,int m,int p)
{
	if(m==0) return 1;
    return (C(n%p,m%p)*Lucas(n/p,m/p,p)%p)%p;
}

Catalan 数

一个定义:给定 \(n\) 个 \(0\) 和 \(n\) 个 \(1\) ,它们按某种顺序排成的长度长为 \(2n\) 的序列,满足任意前缀 \(0\) 的个数都不少于 \(1\) 的个数的序列的数量为 \(Catalan_n=\dfrac{C_{2n}^n}{n+1}\)

概率与期望

概率

设样本空间为 \(\Omega\) ,若对于 \(\Omega\) 中的每一个随机事件 \(A\) ,都存在实数 \(P(A)\) 满足

  • \(P(A)\ge 0\)
  • \(P(\Omega)=1\)
  • 对于若干个两两互斥的事件 \(A_1,A_2,...\) 有 \(\Sigma P(A_i)=P(\cup A_i)\) 其中 \(\cup\) 表并集

则称 \(P(A)\) 是随机事件 \(A\) 发生的概率

期望

若随机变量 \(X\) 的取值有 \(x_1,x_2,...\) ,一个随机事件可表示为 \(X=X_i\) ,其概率为 \(P(X=X_i)=p_i\) ,则称 \(E(x)=\Sigma p_i x_i\) ,为随机变量 \(X\) 的数学期望

亦为随机变量取值与概率的乘积之和

数学期望为线性函数 满足 \(E(aX+bY)=aE(X)+bE(Y)\)

字符串

Trie

struct trie{
	int next[MAX][26],cnt;
    bool exist[MAX];
    void insert(char *s,int len)
    {
    	int p=0;
        for(int i=0;i<len;i++)
        {
        	int c=s[i]-'a';
            if(!next[p][c]) next[p][c]=++cnt;
            p=next[p][c];
        }
        exist[p]=1;
    }
    bool find(char *s,int len)
    {
    	int p=0;
        for(int i=1;i<len;i++)
        {
        	int c=s[i]-'a';
            if(!next[p][c]) return 0;
            p=next[p][c];
        }
        return exist[p];
    }
};

搜索

DFS

int dfs(_state())
{
	if(_end()) return _update(_ans());
    _change(_state());
    _solve(_state());
    dfs(_anotherState());
	_restore(_state());
}

BFS

int bfs()
{
	queue<_stateDATA> q;
	q.push(_startState()); _set(check[_startState()],1);
    while(!q.empty())
    {
        _stateDATA now=q.front(); q.pop();
        if(check[now]) continue;
        check[now]=1;
        _solve(now);
        q.push(_anotherState());
    }
    return _ans();
}

A*

有 开始的代价 \(g(x)\) 到终点的代价 \(h(x)\) 令 \(f(x)=g(x)+h(x)\)

如果 \(h(x)\le h*(x)\) A* 一定能找到最优解

当 \(h=0\) \(A^*\)变为 DFS 当 \(h=0\) 且边权为 \(1\) \(A^*\) 变为 BFS

int bfs()
{
	priority_queue<_stateDATA(_cmpf())> q; //greater_priority_queue
	q.push(_startState(),f(_startState()));
    while(!q.empty())
    {
        _stateDATA now=q.top(); q.pop();
        _solve(now);
    	if(_ans()==_need()) return _ans();
        q.push(_anotherState(),f(_anotherState()));
    }
    return _ans();
}
int f(int x)
{
	return _g(x)+_h(x);
}

迭代加深

int dfs(_state(),deep)
{
    if(deep>_limit()) return _ans();
	if(_end()) return _ans();
    _change(_state());
    _solve(_state());
    dfs(_anotherState(),deep+1);
	_restore(_state());
}

分治

普通分治

int function(int l,int r)
{
	if(_solveEasily()) return _solve();
    mergeFonction(fonction(l,mid),function(mid+1,r));
}

cdq分治

将一个区间 \([l,r]\) 分成 \([l,mid],[mid+1,r]\) 求解左区间对右区间的贡献

重要的是将问题的规模减半

点分治

杂项

枚举

顺序

void solve()
{
	int A=_init();
    _sort(A);
    do{
    	_solve(A);
    }while(next_permutation(A)); //倒序prev_permutation(A)
}

划分

for(int S=0;S<(1<<MAX);S++)
{
	_solve(S);
}

二分

int search(int L,int R)
{
	int l=L,r=R,ans;
    while(l<=r)
    {
    	int mid=(l+r)>>1;
        if(check(mid)) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}
bool check(int mid) {return _check();}

三分

const double esp=1e-6;
//
double search(double L,double R)
{
	double l=L,r=R;
    while(r-l>=esp)
    {
    	double t=(r-l)/3;
        if(solve(l+t)>solve(r-t)) r-=t;
        else l+=r;
    }
    return t;
}
double solve(double x) {return _solve();}    

模拟退火

卡时 while((double)_startTime()<=LIMIT_TIME) SA();

void SA()
{
	double t=_originalT();
	_data _state();
	while(t>_endT())
    {
        _date _newState();
        double now=solve(_newState());
        double delta=now-ans;
        if(_canAccept(now)) _accept(_newState());
        else if(exp(-delta/t)*RAND_MAX>rand()) _accept(_newState());
        t=t*_delta();
    }
}

文件IO

freopen

freopen("xxx.in","r",stdin);
freopen("xxx.out","w",stdout);
//

//
fclose(stdin);
fclose(stdout);

fstream

ifstream fin("xxx.in");
ofstream fout("xxx.out");
//

//
fin.close();
fout.close();
上一篇:考研:C语言复习笔记 [Hex Note]


下一篇:Codeforces Round #679 (Div. 2, based on Technocup 2021 Elimination Round 1)