『考点列表』
Part1 数学相关
1 线性代数
矩阵快速幂
点击查看代码
struct MATR{
int a[N][N], r, c;
MATR() {memset(a, 0, sizeof(a));}
void init() {
for (int i = 1; i <= r; ++i) a[i][i] = 1;
}
MATR operator *(const MATR &x) const {
MATR ret;
ret.r = r, ret.c = x.c;
for (int i = 1; i <= r; ++i)
for (int k = 1; k <= c; ++k)
if (a[i][k]) for (int j = 1; j <= x.c; ++j)
(ret.a[i][j] += 1ll*a[i][k]*x.a[k][j]%MOD) %= MOD;
return ret;
}
};
MATR powMA(MATR x, long long y) {
MATR ret;
ret.r = x.r, ret.c = x.c;
ret.init();
while (y) {
if (y&1) ret = ret*x;
x = x*x, y >>= 1;
} return ret;
}
2 数论
裴蜀定理
\[ax+by=c,x\in Z^*,y\in Z^* \Leftrightarrow gcd(a,b)|c
\]
扩展欧几里得算法
点击查看代码
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL tmp1 = exgcd(b, a%b, x, y), tmp2;
tmp2 = x, x = y, y = tmp2-(a/b)*y;
return tmp1;
}
3 计数类
Lucas定理
点击查看代码
int luc(int x, int y) {
if (x < y) return 0;
if (!x || !y || x == y) return 1;
return 1ll*CC(x%Mod, y%Mod)*luc(x/Mod, y/Mod)%Mod;
}
Part2 数据结构
1 线段树
标准型(加乘)
点击查看代码
int sum[N<<2], taga[N<<2], tagm[N<<2], a[N];
void pushup(int &x, int y, int z) {x = plu(y, z);}
void build(int now, int L, int R) {
tagm[now] = 1;
if (L == R) {
sum[now] = a[L];
return ;
}
segc;
build(lc, L, mid), build(rc, mid+1, R);
pushup(sum[now], sum[lc], sum[rc]);
}
void pushdown(int now, int L, int R) {
segc;
sum[lc] = plu(mul(sum[lc], tagm[now]), mul(taga[now], mid-L+1));
sum[rc] = plu(mul(sum[rc], tagm[now]), mul(taga[now], R-mid));
tagm[lc] = mul(tagm[lc], tagm[now]);
tagm[rc] = mul(tagm[rc], tagm[now]);
taga[lc] = plu(mul(taga[lc], tagm[now]), taga[now]);
taga[rc] = plu(mul(taga[rc], tagm[now]), taga[now]);
taga[now] = 0, tagm[now] = 1;
}
void modifya(int now, int L, int R, int ql, int qr, ll val) {
if (ql <= L && R <= qr) {
sum[now] += val*(R-L+1);
taga[now] += val;
return ;
}
segc;
pushdown(now, L, R);
if (ql <= mid) modifya(lc, L, mid, ql, qr, val);
if (qr > mid) modifya(rc, mid+1, R, ql, qr, val);
pushup(sum[now], sum[lc], sum[rc]);
}
void modifym(int now, int L, int R, int ql, int qr, ll val) {
if (ql <= L && R <= qr) {
sum[now] = mul(sum[now], val);
taga[now] = mul(taga[now], val);
tagm[now] = mul(tagm[now], val);
return ;
}
segc;
pushdown(now, L, R);
if (ql <= mid) modifym(lc, L, mid, ql, qr, val);
if (qr > mid) modifym(rc, mid+1, R, ql, qr, val);
pushup(sum[now], sum[lc], sum[rc]);
}
ll ask(int now, int L, int R, int ql, int qr) {
if (ql <= L && R <= qr) return sum[now];
segc; int ret = 0;
pushdown(now, L, R);
if (ql <= mid) ret = plu(ret, ask(lc, L, mid, ql, qr));
if (qr > mid) ret = plu(ret, ask(rc, mid+1, R, ql, qr));
return ret;
}
兔队线段树
点击查看代码
int cntr[N<<2];
double mx[N<<2];
int calc(int now, int L, int R, double Lim) {
if (L == R) return mx[now] > Lim;
segc;
if (mx[lc] > Lim) return calc(lc, L, mid, Lim)+cntr[now];
else return 0+calc(rc, mid+1, R, Lim);
}
void pushup(int now, int L, int R) {
segc;
mx[now] = max(mx[lc], mx[rc]);
cntr[now] = calc(rc, mid+1, R, mx[lc]);
}
void update(int now, int L, int R, int pos, double val) {
if (L == R) {
mx[now] = val;
return ;
}
segc;
if (pos <= mid) update(lc, L, mid, pos, val);
else update(rc, mid+1, R, pos, val);
pushup(now, L, R);
}
2 ST表(维护任何可并性信息)
点击查看代码
void buildst() {
rep (i, 2, n) lg[i] = lg[i>>1]+1;
rep (j, 1, lg[n]) rep (i, 1, n-(1<<j)+1) st[i][j] = max(st[i][j-1], st[i+(1<<(j-1))][j-1]);
}
inline int queryst(int L, int R) {
int dep = lg[R-L+1];
return max(st[L][dep], st[R-(1<<dep)+1][dep]);
}
Part3 字符串题
1 KMP
点击查看代码
int n, m, tpp[N];
char str1[N], str2[N];
for (int i = 1, j = 0; i < m; ++i) {
while (j > 0 && str2[j+1] != str2[i+1]) j = tpp[j];
if (str2[i+1] == str2[j+1]) ++j;
tpp[i+1] = j;
}
for (int i = 0, j = 0; i < n; ++i) {
while (j > 0 && str2[j+1] != str1[i+1]) j = tpp[j];
if (str1[i+1] == str2[j+1]) ++j;
if (j == m) {
printf("%d\n", i+1-m+1);
j = tpp[j];
}
}
2 Manacher
点击查看代码
scanf("%s", str+1);
int n = strlen(str+1);
rep (i, 1, n) a[2*i-1] = '@', a[2*i] = str[i];
a[2*n+1] = '@', n = 2*n+1;
rep (i, 1, n) R[i] = 0;
R[1] = 1;
int rr = 1, ctr = 1, ans = 0;
rep (i, 1, n) {
if (i <= rr) R[i] = min(rr-i+1, R[2*ctr-i]);
while (i-R[i] >= 0 && i+R[i] <= n && a[i-R[i]] == a[i+R[i]]) ++R[i];
if (i+R[i]-1 > rr) rr = i+R[i]-1, ctr = i;
}
3 SAM
一定一定要注意两倍空间啊!
点击查看代码
struct Node {
int ch[26];
int len, fa;
}a[N];
int pre = 1, cnt = 1;
long long siz[N];
void add(int x) {
int p = pre, np = pre = ++cnt;
a[np].len = a[p].len+1; siz[np] = 1;
for (; p && !a[p].ch[x]; p = a[p].fa) a[p].ch[x] = np;
if (!p) a[np].fa = 1;
else {
int q = a[p].ch[x];
if (a[q].len == a[p].len+1) a[np].fa = q;
else {
int nq = ++cnt; a[nq] = a[q];
a[nq].len = a[p].len+1;
a[q].fa = a[np].fa = nq;
for (; p && a[p].ch[x] == q; p = a[p].fa) a[p].ch[x] = nq;
}
}
}
Part4 图论相关
1 并查集
点击查看代码
int fa[N], tot[N];
int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
int merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) return ;
fa[x] = y, tot[y] += tot[x];
}
2 树上问题
LCA(树上倍增)
点击查看代码
int lg[N], dep[N], fa[N][20];
void init_lg() {rep (i, 1, n) lg[i] = lg[i-1]+(1<<lg[i-1] == i);}
void dfs(int x, int par) {
fa[x][0] = par; dep[x] = dep[par]+1;
rep (i, 1, lg[dep[x]]) fa[x][i] = fa[fa[x][i-1]][i-1];
for (auto y:G[x]) if (y != par) dfs(y, x);
}
int LCA(int x, int y) {
if(dep[x] < dep[y]) swap(x, y);
while (dep[x] > dep[y]) x = fa[x][lg[dep[x]-dep[y]]-1];
if (x == y) return x;
rep (i, lg[dep[x]]-1, 0) if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
树链剖分
点击查看代码
void update1(int x, int y, int w) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
update(1, 1, n, id[top[x]], id[x], w);
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
update(1, 1, n, id[x], id[y], w);
}
void update2(int x, int w) {
update(1, 1, n, id[x], id[x]+siz[x]-1, w);
}
LL query1(int x, int y) {
LL ret = 0;
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
(ret += query(1, 1, n, id[top[x]], id[x])) %= mod;
x = fa[top[x]];
}
if (dep[x] > dep[y]) swap(x, y);
(ret += query(1, 1, n, id[x], id[y])) %= mod;
return ret;
}
LL query2(int x) {
return query(1, 1, n, id[x], id[x]+siz[x]-1);
}
void dfs1(int now, int pre, int depp) {
dep[now] = depp;
fa[now] = pre;
siz[now] = 1;
int mxs = -1;
for (auto y:G[now]) {
if (y == pre) continue ;
dfs1(y, now, depp+1);
siz[now] += siz[y];
if (siz[y] > mxs) son[now] = y, mxs = siz[y];
}
}
void dfs2(int now, int topf) {
id[now] = ++cntn;
val[cntn] = a[now];
top[now] = topf;
if (!son[now]) return ;
dfs2(son[now], topf);
for (auto y:G[now]) {
if (y == fa[now] || y == son[now]) continue ;
dfs2(y, y);
}
}
3 最短路
Dijkstra
点击查看代码
int dis[N];
bool vis[N];
void dij(int S) {
for (int i = 1; i <= n; ++i) dis[i] = Inf;
dis[S] = 0;
priority_queue<pair<int, int> > q;
q.push(mp(0, S));
while (!q.empty()) {
int x = q.top().second; q.pop();
if (vis[x]) continue ;
vis[x] = true;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].to;
if (dis[y] > dis[x]+e[i].w) {
dis[y] = dis[x]+e[i].w;
q.push(mp(-dis[y], y));
}
}
}
}
差分约束(SPFA)
点击查看代码
struct BLCK{
int to, next, w;
}e[N+M]; //注意超级原点连的边
int head[N], ecnt;
void addE(int u, int v, int w) {
e[++ecnt].next = head[u], head[u] = ecnt;
e[ecnt].to = v, e[ecnt].w = w;
}
int dis[N], tot[N];
bool vis[N];
bool spfa(int S) {
queue<int> q;
for (int i = 1; i <= n; ++i) dis[i] = INF;
dis[S] = 0, vis[S] = 1;
q.push(S);
while (!q.empty()) {
int x = q.front(); q.pop(); vis[x] = 0;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].to;
if (dis[x]+e[i].w < dis[y]) {
dis[y] = dis[x]+e[i].w;
tot[y] = tot[x]+1;
if (tot[y] >= n+1) return false;
if (!vis[y]) vis[y] = 1, q.push(y);
}
}
} return true;
}
void cfys() {
for (int i = 1; i <= n; ++i) addE(0, i, 0);
for (int i = 1; i <= m; ++i) {
scanf("%d%d%d", &v, &u, &w);
addE(u, v, w);
}
if (!spfa(0)) printf("NO\n");
else for (int i = 1; i <= n; ++i) printf("%d ", dis[i]);
}
4 最小生成树
Kruskal
点击查看代码
void kruskal() {
for (int i = 1; i <= n; ++i) fa[i] = i;
sort(e+1, e+1+m, cmp);
for (int i = 1; i <= m && cnt < n-1; ++i) {
int fu = find(e[i].u), fv = find(e[i].v);
if (fu == fv) continue ;
++cnt, ans += e[i].w;
fa[fu] = fv;
}
}
Kruskal重构树
点击查看代码
int f[N<<1];
void kru() {
sort(e+1, e+1+m, cmp);
for (int i = 1; i <= 2*n; ++i) fa[i] = i;
int ncnt = n;
for (int i = 1; i <= m; ++i) {
int fu = find(e[i].u), fv = find(e[i].to);
if (fu == fv) continue ;
fa[fu] = fa[fv] = ++ncnt;
add(ncnt, fu), add(fu, ncnt);
add(ncnt, fv), add(fv, ncnt);
val[cnt] = e[i].w;
}
}
5 图连通性
割点
点击查看代码
int ccnt, buc[N], dfn[N], low[N], rt, dn;
void dfs(int now) {
dfn[now] = low[now] = ++dn;
int scnt = 0;
for (int i = head[now]; i; i = e[i].next) {
int y = e[i].to;
if (!dfn[y]) {
++scnt, dfs(y), low[now] = min(low[now], low[y]);
if (low[y] >= dfn[now] && now != rt) ccnt += !buc[now], buc[now] = 1;
} else low[now] = min(low[now], dfn[y]);
}
if (scnt >= 2 && now == rt) ccnt += !buc[now], buc[now] = 1;
}
void getbuc() {
for (int i = 1; i <= n; ++i) if (!dfn[i]) rt = i, dfs(i);
}
强连通分量
点击查看代码
int n, m, col[N], cntc, dfn[N], low[N], vis[N], stc[N], top, dn;
vector<int> G[N], GG[N];
void tarjan(int now) {
vis[now] = 1, dfn[now] = low[now] = ++dn, stc[++top] = now;
for (auto y:G[now]) {
if (!dfn[y]) tarjan(y), low[now] = min(low[now], low[y]);
else if(vis[y]) low[now] = min(low[now], dfn[y]);
}
if (dfn[now] == low[now]) {
col[now] = ++cntc;
while (stc[top] != now) col[stc[top]] = cntc, vis[stc[top--]] = 0;
vis[now] = 0, --top;
}
}
int a[N], val[N];
void shr() {
for (int i = 1; i <= n; ++i) if (!dfn[i]) tarjan(i);
for (int i = 1; i <= n; ++i) {
val[col[i]] += a[i];
for (auto y:G[i])
if (col[i] != col[y]) GG[col[i]].emplace_back(col[y]);
}
}
6 网络流
最大流(Dinic)
点击查看代码
int dis[N], cur[N];
bool bfs() {
for (int i = 1; i <= cntn; ++i) dis[i] = INF, cur[i] = head[i];
queue<int> q;
q.push(S);
dis[S] = 0;
while (!q.empty()) {
int x = q.front(); q.pop();
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].to;
if (e[i].w && dis[y] == INF) {
q.push(y);
dis[y] = dis[x]+1;
if (y == T) return true;
}
}
} return false;
}
int dfs(int x, int flow) {
if (!flow || x == T) return flow;
int used = 0;
for (int &i = cur[x]; i && used < flow; i = e[i].next) {
int y = e[i].to;
if (dis[y] != dis[x]+1) continue ;
int tmp = dfs(y, min(flow-used, e[i].w));
if (tmp) {
e[i].w -= tmp; e[i^1].w += tmp;
used += tmp;
}
} return used;
}
void dinic(int &ans) {while (bfs()) ans += dfs(S, INF);}
最小费用费用流(SPFA+Dinic)
点击查看代码
int dis[N], cur[N];
bool vis[N];
bool spfa() {
for (int i = 1; i <= cntn; ++i) dis[i] = INF, cur[i] = head[i];
queue<int> q;
q.push(S);
dis[S] = 0;
vis[S] = 1;
while (!q.empty()) {
int x = q.front(); q.pop(), vis[x] = 0;
for (int i = head[x]; i; i = e[i].next) {
int y = e[i].to;
if (dis[y] > dis[x]+e[i].c && e[i].w) {
dis[y] = dis[x]+e[i].c;
if (!vis[y]) {
q.push(y);
vis[y] = 1;
}
}
}
} return dis[T] != INF;
}
int dfs(int x, int flow) {
if (x == T || !flow) return flow;
int used = 0;
vis[x] = 1;
for (int &i = cur[x]; i && used < flow; i = e[i].next) {
int y = e[i].to;
if (dis[y] != dis[x]+e[i].c || vis[y]) continue ;
int tmp = dfs(y, min(flow-used, e[i].w));
if (tmp) {
e[i].w -= tmp, e[i^1].w += tmp;
used += tmp;
totc += tmp*e[i].c;
}
}
vis[x] = 0;
return used;
}
int MCCF(int x, int y) {
int ans = 0; S = x, T = y, totc = 0;
while (spfa()) ans += dfs(S, INF);
return totc;
}
Ex 码头
1 define & 常量
点击查看代码
#define mp make_pair
#define vi vector<int>
#define eb emplace_back
#define pli pair<LL, int>
#define segc int mid = L+R>>1, lc = now<<1, rc = lc|1
const int N = 1e5+5, MOD = 998244353, INF = 1e9;
typedef long long LL;
2 快速I/O
点击查看代码
inline int read() {
int x = 0, f = 1; char ch = 0;
while (!isdigit(ch)) {ch = getchar();if (ch == '-') f = -1;}
while (isdigit(ch)) x = (x<<3)+(x<<1)+(ch^48), ch = getchar();
return f*x;
}
inline void put(int x) {
int num=0; char c[15];
if (x == 0) {puts("0"); return ;}
if (x < 0) putchar('-'),x = -x;
while (x) c[++num] = (x%10)+48, x /= 10;
while (num) putchar(c[num--]);
putchar('\n');
}
3 封装加乘幂
点击查看代码
int plu(int x, int y) {return x += y, x >= MOD && (x -= MOD), x;}
void plut(int &x, int y) {x += y, x >= MOD && (x -= MOD);}
int mul(int x, int y) {return 1ll*x*y%MOD;}
int powM(int x, int y = MOD-2) {
int ret = 1;
while (y) {
if (y&1) ret = mul(ret, x);
x = mul(x, x), y >>= 1;
} return ret;
}
4 组合数
点击查看代码
int fac[N], ifac[N];
int CC(int x, int y) {
if (x < y) return 0;
return mul(mul(fac[x], ifac[y]), ifac[x-y]);
}
void init_fac() {
for (int i = fac[0] = 1; i <= N-5; ++i) fac[i] = mul(fac[i-1], i);
ifac[N-5] = powM(fac[N-5]);
for (int i = N-6; ~i; --i) ifac[i] = mul(ifac[i+1], i+1);
}
5 总集
点击查看代码
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
#define mp make_pair
#define vi vector<int>
#define eb emplace_back
#define pli pair<LL, int>
#define fi first
#define se second
#define rep(rp,a,b) for(int rp=a;rp<=b;++rp)
#define per(bl,a,b) for(int bl=a;bl>=b;--bl)
#define segc int mid = L+R>>1, lc = now<<1, rc = lc|1
const int N = 1e5+5, MOD = 998244353, INF = 1e9;
inline int read() {
int x = 0, f = 1; char ch = 0;
while (!isdigit(ch)) {ch = getchar(); if (ch == '-') f = -1;}
while (isdigit(ch)) x = (x<<3)+(x<<1)+(ch^48), ch = getchar();
return f*x;
}
inline void put(int x) {
int num=0; char c[15];
if (x == 0) {puts("0"); return ;}
if (x < 0) putchar('-'), x = -x;
while (x) c[++num] = (x%10)+48, x /= 10;
while (num) putchar(c[num--]);
putchar('\n');
}
inline int plu(int x, int y) {return x += y, x >= MOD && (x -= MOD), x;}
inline int mul(int x, int y) {return 1ll*x*y%MOD;}
int powM(int x, int y = MOD-2) {
int ret = 1;
while (y) {
if (y&1) ret = mul(ret, x);
x = mul(x, x), y >>= 1;
} return ret;
}
int fac[N], ifac[N], pw2[N];
int C(int x, int y) {
if (x < y) return 0;
return mul(mul(fac[x], ifac[y]), ifac[x-y]);
}
void init() {
fac[0] = pw2[0] = 1;
rep (i, 1, N-5) fac[i] = mul(fac[i-1], i);
ifac[N-5] = powM(fac[N-5]);
per (i, N-6, 0) ifac[i] = mul(ifac[i+1], i+1);
rep (i, 1, N-5) pw2[i] = pw2[i-1]*2%MOD;
}
// ---------- templates above ----------