ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

c++算法竞赛常用板子集合(持续更新)

2022-10-24 11:48:01  阅读:251  来源: 互联网

标签:C++ spdlog  光速入门 logger 简单 最快 c语言 方式 程序 退出


前言

本文主要包含算法竞赛一些常用的板子,码风可能不是太好,还请见谅。

后续会继续补充没有的板子。当然我太菜了有些可能写不出来T^T

稍微有些分类但不多,原谅我QwQ

建议 Ctrl + F 以快速查找板子。

常用板子

树状数组

此处为查询区间和的树状数组。

int bit[500010];
void add(int k, int x) {
	while (k <= n) {
		bit[k] += x;
		k += lowbit(k);
	}
}
int ask(int k) {
	int res = 0;
	while (k) {
		res += bit[k];
		k -= lowbit(k);
	}
	return res;
}

线段树

此处为区间修改区间查询区间和的线段树。

struct SegmentTree {
	ll sum[N << 2], lazy[N << 2];
	int l[N << 2], r[N << 2];
	void update(int rt) {
		sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
	}
	void pushdown(int rt) {
		if (!lazy[rt]) return ;
		sum[rt << 1] += (r[rt << 1] - l[rt << 1] + 1) * lazy[rt], lazy[rt << 1] += lazy[rt];
		sum[rt << 1 | 1] += (r[rt << 1 | 1] - l[rt << 1 | 1] + 1) * lazy[rt], lazy[rt << 1 | 1] += lazy[rt];
		lazy[rt] = 0;
		update(rt);
	}
	void build(int rt, int L, int R) {
		l[rt] = L, r[rt] = R;
		if (L == R) {
			sum[rt] = a[L];
			return ;
		}
		int mid = L + R >> 1;
		build(rt << 1, L, mid), build(rt << 1 | 1, mid + 1, R);
		update(rt);
	}
	void change(int rt, int L, int R, int x) {
		if (L <= l[rt] && r[rt] <= R) {
			sum[rt] += (r[rt] - l[rt] + 1) * x;
			lazy[rt] += x;
			return ;
		}
		pushdown(rt);
		if (L <= r[rt << 1]) change(rt << 1, L, R, x);
		if (l[rt << 1 | 1] <= R) change(rt << 1 | 1, L, R, x);
		update(rt);
	}
	ll query(int rt, int L, int R) {
		if (L <= l[rt] && r[rt] <= R) return sum[rt];
		pushdown(rt);
		ll res = 0;
		if (L <= r[rt << 1]) res += query(rt << 1, L, R);
		if (l[rt << 1 | 1] <= R) res += query(rt << 1 | 1, L, R);
		return res;
	}
} tree;

不是吧真有人手写堆吗

ll q[N], cnt;
void pushup(int id) {
	while (id > 1) {
		if (q[id] >= q[id >> 1]) break;
		swap(q[id], q[id >> 1]);
		id >>= 1;
	}
}
void movedown() {
	int id = 1;
	while (id << 1 <= cnt) {
		if ((id << 1 | 1) <= cnt) {
			if (q[id] < min(q[id << 1], q[id << 1 | 1])) break;;
			if (q[id << 1] < q[id << 1 | 1]) swap(q[id], q[id << 1]), id <<= 1;
			else swap(q[id], q[id << 1 | 1]), id = id << 1 | 1;
		}
		else {
			if (q[id] > q[id << 1]) swap(q[id], q[id << 1]);
			break;
		}
	}
}
void add(ll x) {
	q[++cnt] = x;
	pushup(cnt);
}
void pop() {
	swap(q[1], q[cnt]);
	cnt--;
	movedown();
}

并查集

struct Disjoint_Set {
	int p[N], size[N];
	void build() {
		for (int i = 1; i <= n; i++) p[i] = i, size[i] = 1;
	}
	int root(int x) {
		if (p[x] != x) return p[x] = root(p[x]);
		return x;
	}
	void merge(int x, int y) {
		x = root(x), y = root(y);
		if (size[x] > size[y]) swap(x, y);
		p[x] = y;
		size[y] += size[x];
	}
	bool check(int x, int y) {
		x = root(x), y = root(y);
		return x == y;
	}
} a;

ST表

代码实现查询区间 [l,r][l,r] 的区间最大值

for (int i = 1; i <= n; i++) st[0][i] = a[i];
for (int j = 1; j <= lg; j++) {
	for (int i = 1; i <= n - (1 << j) + 1; i++) {
		st[j][i] = max(st[j - 1][i], st[j - 1][i + (1 << (j - 1))]);
	}
}
int l, r, lg2, len;
for (int i = 1; i <= m; i++) {
	l = read(), r = read();
	lg2 = log2(r - l + 1);
	len = 1 << lg2;
	printf("%d\n", max(st[lg2][l], st[lg2][r - len + 1]));
}

边链表

const int N = 100010;
int last[N], cnt;
struct edge {
	int to, next, w;
} e[N << 1];
void addedge(int x, int y, int w) {
	e[++cnt].to = y;
	e[cnt].next = last[x];
	e[cnt].w = w;
	last[x] = cnt;
}

LCA

此处贴的是 Tarjan法 求LCA。更多方法

struct Disjoint_Set {
	int p[N], size[N];
	void build() {
		for (int i = 1; i <= n; i++) p[i] = i, size[i] = 1;
	}
	int root(int x) {
		if (p[x] != x) return p[x] = root(p[x]);
		return x;
	}
	void merge(int x, int y) {
		x = root(x), y = root(y);
		if (size[x] > size[y]) swap(x, y);
		p[x] = y;
		size[y] += size[x];
	}
	bool check(int x, int y) {
		x = root(x), y = root(y);
		return x == y;
	}
} a;
int last[N], cnt;
struct edge {
	int to, next;
} e[N << 1];
void addedge(int x, int y) {
	e[++cnt].to = y;
	e[cnt].next = last[x];
	last[x] = cnt;
}
struct node {
	int x, y, ans;
} ask[N];
vector <int> g[N];
int p[N];
bool vis[N];
int r[N];
void dfs(int x, int f) {
	p[x] = f;
	for (int i = last[x]; i; i = e[i].next) {
		int v = e[i].to;
		if (v == f) continue;
		vis[v] = 1;
		for (int j : g[v]) {
			int o = ask[j].x;
			if (o == v) o = ask[j].y;
			if (!vis[o]) continue;
			ask[j].ans = r[a.root(o)]; 
		}
		dfs(v, x);
		a.merge(x, v);
		r[a.root(x)] = x;
	}
}

单源最短路(Dijkstra)

这里是堆优化版呢。笑了有些时候堆优化还没不优化好

void dij(int s) {
	priority_queue <pii, vector<pii>, greater<pii> > q; 
	memset(dis, 0x7f7f7f7f, sizeof(dis));
	q.push({0, s});
	dis[s] = 0;
	while (!q.empty()) {
		pii u = q.top(); q.pop();
		int pos = u.second;
		if (vis[pos]) continue;
		vis[pos] = 1;
		for (int j = last[pos]; j; j = e[j].next) {
			int v = e[j].to;
			if (vis[v]) continue;
			if (dis[pos] + e[j].w < dis[v]) dis[v] = dis[pos] + e[j].w, q.push({dis[v], v});
		}
	}

缩点

其中 pp 为缩点后的新点。

int dfn[N], low[N], dcnt;
bool instack[N];
stack <int> s;
int p[N], h[N];
void dfs(int x, int f) {
	instack[x] = 1;
	s.push(x);
	dfn[x] = low[x] = ++dcnt;
	for (int i = last[0][x]; i; i = e[0][i].next) {
		int v = e[0][i].to;
		if (dfn[v]) {
			if (instack[v]) low[x] = min(low[x], dfn[v]);
			continue;
		}
		dfs(v, x);
		low[x] = min(low[x], low[v]);
	}
	if (low[x] >= dfn[x]) {
		p[x] = x, h[x] = a[x], instack[x] = 0;
		while (s.top() != x) {
			p[s.top()] = x;
			h[x] += a[s.top()];
			instack[s.top()] = 0;
			s.pop();
		}
		s.pop();
	}
}

欧拉路径

int st[N], ed[N];
struct edge {
	int u, v;
} e[N << 1];
int rd[N], cd[N];
bool cmp(edge x, edge y) {
	if (x.u != y.u) return x.u < y.u;
	return x.v < y.v;
}
int ans[N << 1], cnt;
void dfs(int x) {
	while (st[x] <= ed[x]) {
		st[x]++;
		dfs(e[st[x] - 1].v);
	}
	ans[++cnt] = x;
}

乘法逆元

fac[0] = fac[1] = 1;
for (int i = 2; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
inv[1] = 1;
for (int i = 2; i <= n; i++) inv[i] = (mod - mod / i) * inv[mod % i] % mod;

快速幂

ll qpow(ll a, ll b) {
	ll res = 1;
	while (b) {
		if (b & 1) res = res * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return res;
}

矩阵快速幂

不是我说这写的是真的丑,凑活着看吧QAQ

struct sq {
	ll x[110][110];
	void build() {
		for (int i = 1; i <= n; i++) x[i][i] = 1;
	}
	void dd() {
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				x[i][j] = 0;
	}
} a, ans;
sq operator *(const sq &x, const sq &y) {
	sq res;
	res.dd();
	for (int k = 1; k <= n; k++)
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				res.x[i][j] = (res.x[i][j] + x.x[i][k] * y.x[k][j] % mod) % mod;
	return res;
}
void qpow(ll x) {
	while (x) {
		if (x & 1) ans = ans * a;
		a = a * a;
		x >>= 1;
	}
}

线性基

pp 数组表示基底,xx 为添加进的数字。

int p[N];
void add(ll x) {
	for (int i = N; i >= 0; i--) {
		if (!(x & (1ll << i))) continue;
		if (p[i]) x ^= p[i];
		else {p[i] = x; return ;}
	}
}

线性筛

int prime[6000010], cnt;
bool isprime[N + 10];
void prim() {
	isprime[0] = isprime[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (!isprime[i]) prime[++cnt] = i;
		for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
			isprime[i * prime[j]] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}

字符串哈希

int Char(char c) {
	if (c >= '0' && c <= '9') return c - '0' + 1; //0~9: 1~10
	if (c >= 'a' && c <= 'z') return c - 'a' + 11; //a~z: 11~37
	if (c >= 'A' && c <= 'Z') return c - 'A' + 38; //A~Z: 38~65
	return 0;
}
map <ll, int> mp;

cin >> s;
ll x = 0;
for (int i = 0; i < s.size(); i++) x = (x * 100) + Char(s[i]);
mp[x] = 1;

KMP

ss 和 tt 为需要匹配的两个 char 类型数组。

borderiborderi 表示 tt 长度为 ii 的前缀最长的 borderborder 长度。

完了border是啥来着?

ls = strlen(s + 1), lt = strlen(t + 1);
int j = 0;
for (int i = 2; i <= lt; i++) {
	while (j >= 1 && t[j + 1] != t[i]) j = border[j];
	if (t[j + 1] == t[i]) j++;
	border[i] = j;
}
int sx = 1, tx = 0;
while (sx <= ls) {
	while (tx >= 1 && s[sx] != t[tx + 1]) tx = border[tx];
	if (t[tx + 1] == s[sx]) tx++;
	if (tx == lt) printf("%d\n", sx - lt + 1);
	sx++;
}

AC自动机

struct Trie {
	int id[27], cnt, fail;
} t[N];
void Build(string &s) {
	int now = 0;
	for (int i = 0; i < s.size(); i++) {
		if (!t[now].id[s[i] - 'a']) t[now].id[s[i] - 'a'] = ++cnt;
		now = t[now].id[s[i] - 'a'];
	}
	t[now].cnt++;
}
void Fail() {
	queue <int> q;
	for (int i = 0; i < 26; i++) {
		int v = t[0].id[i];
		if (v != 0) {
			t[v].fail = 0;
			q.push(v);
		}
	}
	while (!q.empty()) {
		int u = q.front(); q.pop();
		for (int i = 0; i < 26; i++) {
			int v = t[u].id[i];
			if (v != 0) {
				t[v].fail = t[t[u].fail].id[i];
				q.push(v);
			}
			else t[u].id[i] = t[t[u].fail].id[i];
		}
	}
}
string s;
int ans;
void Query() {
	int now = 0;
	for (int i = 0; i < s.size(); i++) {
		now = t[now].id[s[i] - 'a'];
		for (int to = now; to; to = t[to].fail) {
			if (t[to].cnt == -1) break;
			ans += t[to].cnt;
			t[to].cnt = -1;
		}
	}
}

标签:C++,spdlog,,光速入门,logger,简单,最快,c语言,方式,程序,退出
来源:

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有