CSP-S考前整理合集

复赛前整理整理……涨涨RP

本文主要收集整理各类板子的代码。


一·IO优化

快速读入

template<class T>
inline T read(T &x) {
	x = 0; int w = 1, ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
	return x *= w;
}

输出

输出不再写单独函数

答案输出用printf,调试信息一律std::cout<<……

二·数论

欧几里得算法

用处:求最大公约数

int gcd(int a, int b) {return !b ? a : gcd(b, a % b);}

int main() {
	int a, b;
	read(a), read(b);
	int G = gcd(a, b);
}

时间复杂度:\(O(logn)\)

扩展欧几里得算法

用处:求解逆元,解线性同余方程……

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

int main() {
	int a, b, x, y;
	exgcd(a, p, x, y);
	int G = (x % p + p) % p; // a 在 %p意义下的逆元
}

时间复杂度:\(O(logn)\)

快速幂

用处:求解逆元……

#define LL long long
const int MOD = 1e9 + 7;

LL quick_pow(LL a, LL b, LL mod) {
	LL ans = 1;
	while (b) {
		if (b & 1) ans = ans * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return ans % mod;
}

int main() {
	LL a, b;
	LL G = quick_pow(a, b, MOD); // a ^ b % MOD 
}

时间复杂度:\(O(logn)\)

龟速乘

用处:乘法爆\(long\ long\)时使用

#define LL long long
const int MOD = 1e9 + 7;

LL quick_mul(LL a, LL b, LL mod) {
	LL ans = 1;
	while (b) {
		if (b & 1) ans = (ans + a) % mod;
		b >>= 1;
		a = (a + a) % mod;
	}
	return ans % mod;
}

int main() {
	LL a, b;
	LL G = quick_mul(a, b, MOD); // a * b % MOD 
}

时间复杂度:\(O(logn)\)

逆元

用处:计算\(\frac{a}{b}mod\ P\)

#define LL long long
const int maxn = 3e5 + 5;
const int MOD = 1e9 + 7;
int inv[maxn];

LL quick_pow(LL a, LL b, LL mod) {
	LL ans = 1;
	while (b) {
		if (b & 1) ans = ans * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return ans % mod;
}

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


int main() {
	LL a;
	LL G = quick_pow(a, MOD - 2, MOD); // a 在模MOD意义下逆元, a必须与MOD互质

	LL x, y;
	exgcd(a, MOD, x, y);
	LL G = (x % MOD + MOD) % MOD // a 在模MOD意义下逆元,无特殊要求

	LL n;
	inv[1] = 1;
	for (int i = 2; i <= n; i++) inv[i] = (MOD - MOD / i) * inv[MOD % i] % MOD; 
	// 线性求逆元 
}

时间复杂度:前两种均为\(O(logn)\),最后一种为\(O(n)\)

质因数分解

用处:求一个数的质因数,欧拉函数值……

const int maxn = 3e5 + 5;
int p[maxn], c[maxn], cnt;
void divede(int n) {
	for (int i = 2; i * i <= n; i++) 
		if (n % i == 0) {
			p[++cnt] = i;
			while (n % i == 0) c[i]++, n /= i;
		}
	if (n > 1) p[++cnt] = n, c[n] = 1;
}

int main() {
	int n;
	divide(n);
}

时间复杂度:\(O(\sqrt n)\)

线性筛法求素数及积性函数

线性筛素数

const int maxn = 3e5 + 5;
int prime[maxn], is_prime[maxn], cnt;

void euler_phi(int n) {
	is_prime[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (!is_prime[i]) prime[++cnt] = i;
		for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
			is_prime[i * prime[j]] = 1;
			if (i % prime[j] == 0) break;
		}
	}
}

int main() {
	euler_phi(maxn);
}

线性筛欧拉函数

const int maxn = 3e5 + 5;
int prime[maxn], phi[maxn], cnt;

void phi_table(int n) {
	phi[1] = phi[0] = 0;
	for (int i = 2; i <= n; i++) {
		if (!phi[i]) 
			prime[++cnt] = i, phi[i] = i - 1;
		for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
			if (i % prime[j] == 0) {
				phi[i * prime[j]] = phi[i] * prime[j];
				break;
			}
			phi[i * prime[j]] = phi[i] * (prime[j] - 1);
		}
	}
}

int main() {
	phi_table(maxn);
}

线性筛约数个数

const int maxn = 3e5 + 5;
int prime[maxn], is_prime[maxn], d[maxn], st[maxn], cnt;

void phi_table(int n) {
	is_prime[1] = 1, d[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (!is_prime[i]) 
			prime[++cnt] = i, d[i] = 2, st[i] = 1;
		for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
			is_prime[i * prime[j]] = 1;
			if (i % prime[j] == 0) {
				d[i * prime[j]] = d[i] / (st[i] + 1) * (st[i] + 2);
				st[i * prime[j]] = st[i] + 1; break;
			}
			d[i * prime[j]] = d[i] * d[prime[j]], st[i * prime[j]] = 1;
		}
	}
}

int main() {
	phi_table(maxn);
}

线性筛约数和

const int maxn = 3e5 + 5;
int prime[maxn], is_prime[maxn], sd[maxn], st[maxn], cnt;

void phi_table(int n) {
	is_prime[1] = 1, sd[1] = 1;
	for (int i = 2; i <= n; i++) {
		if (!is_prime[i]) 
			prime[++cnt] = i, sd[i] = i + 1, st[i] = i + 1;
		for (int j = 1; j <= cnt && i * prime[j] <= n; j++) {
			is_prime[i * prime[j]] = 1;
			if (i % prime[j] == 0) {
				sd[i * prime[j]] = sd[i] / st[i] + 1 * (st[i] * prime[j] + 1);
				st[i * prime[j]] = st[i] * prime[j] + 1; break;
			}
			sd[i * prime[j]] = sd[i] * sd[prime[j]], st[i * prime[j]] = prime[j] + 1;
		}
	}
}

int main() {
	phi_table(maxn);
}

时间复杂度:以上代码复杂度均为\(O(n)\)

中国剩余定理

用处:求解模数互质的同余方程组

#define LL long long
const int maxn = 3e5 + 5;
LL a[maxn], m[maxn];


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

LL China(int n, LL *a, LL*m) {
	LL G = 1, ans = 0, x, y;
	for (int i = 0; i < n; i++) G *= m[i];
	for (int i = 0; i < n; i++) {
		LL w = G / m[i];
		exgcd(w, m[i], x, y);
		x = (x % m[i] + m[i]) % m[i];
		ans = (ans + x * w * a[i]) % G;
	}
	return (ans + G) % G;
}

int main() {
	int n;
	scanf("%d", &n);
	for (int i = 0; i < n; i++) scanf("%lld%lld", &a[i], &m[i]);
	printf("%lld\n", China(n, a, m));
}

时间复杂度:\(O(nlogn)\)

整除分块

用处:求解类似于\(\sum_{i=1}^{n}f(i)\lfloor\frac{n}{i}\rfloor\)的式子,其中\(f(i)\)应该可以用前缀和预处理

#define LL long long

int main() {
	for (LL l = 1, r; l <= n; l = r + 1) {
		r = n / (n / l);
		ans = (r - l + 1) * (n / l);
	}
	printf("%lld\n", ans);
}

时间复杂度:\(O(\sqrt n)\)

组合数

int quick_pow(int a, int b, int mod) {
	int ans = 1;
	while (b) {
		if (b & 1) ans = ans * a % mod;
		b >>= 1;
		a = a * a % mod;
	}
	return ans % mod;
}

void init() {
	fac[0] = 1;
	for (int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % MOD;
	for (int i = 1; i <= n; i++) ifac[i] = quick_pow(fac[i], MOD - 2, MOD);
}

int C(int n, int m) {
	return fac[n] % MOD * ifac[m] % MOD * ifac[n - m] % MOD;
}

三·图论

图的存储与遍历

链式前向星

const int maxn = 3e5 + 5;
int n, m, tot, head[maxn];

struct Edge {
	int to, val, nxt;
	Edge(int _to, int _val, int _nxt) {
		this -> to = _to;
		this -> val = _val;
		this -> nxt = _nxt;
	} Edge(){}
}edge[maxn << 1];

void add(int from, int to, int val) {edge[++tot] = Edge(to, val, head[from]), head[from] = tot;}

void dfs(int u, int f) {
	for (int i = head[u]; i; i = edge[i].nxt) {
		int v = edge[i].to;
		if (v == f) continue;
		dfs(v, u);
	}
}

时间复杂度:\(O(n)\)

vector

using std::pair;
using std::make_pair;
using std::vector;
#define pii pair<int, int>
const int maxn = 3e5 + 5;

vector<pii >G[maxn];

void add(int from, int to, int val) {
	G[from].push_back(make_pair(to, val));
	G[to].push_back(make_pair(from, val));
}

void dfs(int u, int f) {
	for (int i = 0; i < G[u].size(); i++) {
		int v = G[u][i];
		if (v == f) continue;
		dfs(v, u);
	}
}

时间复杂度:\(O(n)\),常数略大……

最短路算法

Floyd多源最短路

int g[1007][1007];

void init() {
	for (int i = 1; i <= n; i++)
		for (int j = 1; j <= n; j++)
			if (i == j) g[i][j] = 0;
			else g[i][j] = 0x3f3f3f3f;
	for (int i = 1; i <= m; i++) {
		int x, y, z;
		read(x), read(y), read(z);
		g[x][y] = g[y][x] = min(g[x][y], z);		
	}
}

void Floyd() {
	for (int k = 1; k <= n; k++) 
		for (int i = 1; i <= n; i++)
			for (int j = 1; j <= n; j++)
				g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}

时间复杂度:\(O(n^3)\)

Dijkstra单源最短路

using std::priority_queue;
const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dis[maxn], vis[maxn];

struct Edge {
	int to, val, nxt;
	Edge(int _to, int _val, int _nxt) {
		this -> to = _to;
		this -> val = _val;
		this -> nxt = _nxt;
	} Edge(){}
}edge[maxn << 1];

void add(int from, int to, int val) {edge[++tot] = Edge(to, val, head[from]), head[from] = tot;}

struct Node {
	int w, now;
	bool operator < (const Node &rhs) const {
		return w > rhs.w;
	}
};

void djikstra(int S) {
	memset(dis, 0x3f, sizeof(dis));
	memset(vis, 0, sizeof(0));
	priority_queue<Node>pq;
	pq.push((Node){0, S}), dis[S] = 0;
	while (!pq.empty()) {
		Node x = pq.top(); pq.pop();
		int u = x.now();
		if (vis[u]) continue;
		vis[u] = 1;
		for (int i = head[u]; i; i = edge[i].nxt) {
			int v = edge[i].to;
			if (dis[v] > dis[u] + edge[i].val) {
				dis[v] = dis[u] + edge[i].val;
				pq.push((Node){dis[v], v});
			}
		}
	}
}

Spfa单源最短路

using std::queue;
const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dis[maxn], vis[maxn];

struct Edge {
	int to, val, nxt;
	Edge(int _to, int _val, int _nxt) {
		this -> to = _to;
		this -> val = _val;
		this -> nxt = _nxt;
	} Edge(){}
}edge[maxn << 1];

void add(int from, int to, int val) {edge[++tot] = Edge(to, val, head[from]), head[from] = tot;}

void spfa(int S) {
	memset(dis, 0x3f, sizeof(dis));
	memset(vis, 0, sizeof(vis));
	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 = edge[i].nxt) {
			int v = edge[i].to;
			if (dis[v] > dis[u] + edge[i].val) {
				dis[v] = dis[u] + edge[i].val;
				if (!vis[v]) {
					vis[v] = 1;
					q.push(v);
				}
			}
		}
	} 
}

并查集

严格讲貌似不属于图论,不过就在这里放着吧……

int fa[maxn];

struct DSU {
	int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
	bool judge(int x, int y) {return find(x) == find(y);}
	void merge(int x. int y) {fa[find(x)] = find(y);}
}T;

void init() {
	for (int i = 1; i <= n; i++) fa[i] = i;
	......
}

时间复杂度:\(O(nlogn)\)

Kruskal最小生成树算法

用处:求最小生成树

struct Edge {
	int from, to, val;
	bool operator < (const Edge &rhs) const {
		return val < rhs.val;
	}
}edge[maxn];

int fa[maxn];

struct DSU {
	int find(int x) {return x == fa[x] ? x : fa[x] = find(fa[x]);}
	bool judge(int x, int y) {return find(x) == find(y);}
	void merge(int x. int y) {fa[find(x)] = find(y);}
}T;

int main() {
	read(n), read(m);
	for (int i = 1; i <= m; i++) read(edge[i].from), read(edge[i].to), read(edge[i].val);
		std::sort(edge + 1, edge + m + 1);
	for (int i = 1; i <= n; i++) fa[i] = i;
	int sum = 0, cnt = 0;
	for (int i = 1; i <= m; i++) {
		int x = edge[i].from, y = edge[i].to;
		if (T.judge(x, y)) continue;
		T.merge(x, y);
		sum += edge[i].val;
		++cnt;
		if (cnt == n - 1) break;
	}
	printf("%d\n", sum);
}

最近公共祖先

倍增LCA

const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dep[maxn], fa[maxn][22], lg[maxn];

struct Edge {
	int to, nxt;
	Edge (int _to, int _nxt) {
		this -> to = _to;
		this -> nxt = _nxt;
	} Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

void dfs(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 = edge[i].nxt) {
		int v = edge[i].to;
		if (v == f) continue;
		dfs(v, u);
	}
}

int query(int x, int y) {
	if (dep[x] < dep[y]) std::swap(x, y);
	while (dep[x] > dep[y])
		x = fa[x][lg[dep[x] - dep[y]] - 1];
	if (x == y) return x;
	for (int j = lg[dep[x]] - 1; j >= 0; j--)
		if (fa[x][j] != fa[y][j])
			x = fa[x][j], y = fa[y][j];
	return fa[x][0];
}

int main() {
	for (int i = 1; i <= n; i++) lg[i] = lg[i - 1] + ((1 << lg[i - 1]) == i);
	......
}

时间复杂度:\(O(nlogn)\)

树剖LCA

const int maxn = 3e5 + 5;
int n, m, tot, head[maxn], dep[maxn], siz[maxn], fa[maxn], son[maxn], top[maxn];

struct Edge {
	int to, nxt;
	Edge (int _to, int _nxt) {
		this -> to = _to;
		this -> nxt = _nxt;
	} Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

void dfs1(int u, int f, int deep) {
	dep[u] = deep, fa[u] = f, siz[u] = 1;
	for (int i = head[u]; i; i = edge[i].nxt) {
		int v = edge[i].to;
		if (v == f) continue;
		dfs1(v, u, deep + 1);
		siz[u] += siz[v];
		if (siz[v] > siz[son[u]]) son[u] = v;
	}
}

void dfs2(int u, int topf) {
	top[u] = topf;
	if (!son[u]) return ;
	dfs2(son[u], topf);
	for (int i = head[u]; i; i = edge[i].nxt) {
		int v = edge[i].to;
		if (v == fa[u] || v == son[u]) continue;
		dfs2(v, v);
	}
}

int query(int x, int y) {
	while (top[x] != top[y]) {
		if (dep[top[x]] < dep[top[y]]) std::swap(x, y);
		x = fa[top[x]];
	}
	return dep[x] < dep[y] ? x : y;
}

int main() {
	......
	dfs1(1, 0, 1);
	dfs2(1, 1);
	......
}

Tarjan 算法

割点/割顶

const int maxn = 3e5 + 5;
int n, m, tot, num, head[maxn], dfn[maxn], low[maxn], cnt[maxn];

struct Edge {
	int to, nxt;
	Edge (int _to, int _nxt) {
		this -> to = _to;
		this -> nxt = _nxt;
	} Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

void tarjan(int u, int f) {
	dfn[u] = low[u] = ++num;
	int flag = 0;
	for (int i = head[u]; i; i = edge[i].nxt) {
		int v = edge[i].to;
		if (!dfn[v]) {
			tarjan(v, fa);
			low[u] = min(low[u], low[v]);
			if (low[v] >= dfn[u] && x != fa) cnt[u] = 1;
			if (x == fa) flag++;
		}
		low[u] = min(low[u], dfn[v]);
	}
	if (x == fa && flag >= 2) cnt[u] = 1;
}

割边

const int maxn = 3e5 + 5;
int n, m, tot, num, head[maxn], dfn[maxn], low[maxn], bridge[maxn];

struct Edge {
	int to, nxt;
	Edge (int _to, int _nxt) {
		this -> to = _to;
		this -> nxt = _nxt;
	} Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

void tarjan(int u, int in_edge) {
	dfn[u] = low[u] = ++num;
	for (int i = head[u]; i; i = edge[i].nxt) {
		int v = edge[i].to;
		if (!dfn[v]) {
			tarjan(v, i);
			low[u] = min(low[u], low[v]);
			if (low[v] > dfn[u]) bridge[i] = bridge[i ^ 1] = 1;
		}
		else if (i != (in_edge ^ 1))
			low[u] = min(low[u], dfn[v]);
	}
}

int main() {
	tot = 1;
	......
}

强连通分量

const int maxn = 3e5 + 5;
int n, m, tot, num, num_node, head[maxn], dfn[maxn], low[maxn], st[maxn], belong[maxn], cnt, vis[maxn];

struct Edge {
	int to, nxt;
	Edge (int _to, int _nxt) {
		this -> to = _to;
		this -> nxt = _nxt;
	} Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

void tarjan(int u, int in_edge) {
	dfn[u] = low[u] = ++num;
	st[++cnt] = u; vis[u] = 1;
	for (int i = head[u]; i; i = edge[i].nxt) {
		int v = edge[i].to;
		if (!dfn[v]) {
			tarjan(v);
			low[u] = min(low[u], low[v]);
		}
		else if (vis[v])
			low[u] = min(low[u], dfn[v]);
	}
	if (dfn[u] == low[u]) {
		int y;
		++num_node;
		while (y = st[cnt--]) {
			belong[y] = num_node;
			vis[y] = false;
			if (u == y) break; 
		}
	}
}

int main() {
	......
}

时间复杂度:上述代码均为\(O(n)\)

二分图最大匹配

匈牙利算法

用处:据长者说,\(CSP\)可能要考……

#include <iostream>
#include <cstdio>
#include <cstring>

bool vis[1005];
int n, m, e, a[1005][1005], belong[1005];

template<class T>
inline T read(T &x) {
	x = 0; int w = 1, ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
	return x *= w;
} 

bool judge(int x) {
	for (int i = 1; i <= m; i++)
		if (a[x][i] && !vis[i]) {
			vis[i] = 1;
			if (!belong[i] || judge(belong[i])) {
				belong[i] = x;
				return true;
			}
		}
	return false;
}

int main() {
	read(n), read(m), read(e);
	for (int i = 1; i <= e; i++) {
		int x, y;
		read(x), read(y);
		a[x][y] = 1;
	}
	int ans = 0;
	for (int i = 1; i <= n; i++) {
		memset(vis, false, sizeof(vis));
		if (judge(i)) ++ans;
	}
	std::cout << ans << '\n';
	return 0;
}

时间复杂度:\(O(n^2m)\)

Dinic实现二分图匹配

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#define min(a, b) ((a) < (b) ? (a) : (b))
 
const int maxn = 1e6 +5;
const int INF = 1e9;
int n, m, e, head[maxn], tot = -1, s, t;
int maxflow, dep[maxn];
std::queue<int>q;

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x *= w;
}

struct Edge {
    int to, val, nxt;
    Edge(int y, int w, int next) {
        to = y, val = w, nxt = next;
    }
    Edge(){
    }
}edge[maxn << 1];

void add(int from, int to, int val)
{
    edge[++tot] = Edge(to, val, head[from]); head[from] = tot;
}

bool bfs(int s, int t)
{
    memset(dep, 0x7f, sizeof(dep));
    while (!q.empty()) q.pop();
    dep[s] = 0;
    q.push(s);
    while (!q.empty()) {
        int u = q.front();
        q.pop();
        for (int i = head[u]; i != -1; i = edge[i].nxt) {
            int v = edge[i].to;
            if (dep[v] > INF && edge[i].val) {
                dep[v] = dep[u] + 1;
                q.push(v);
                if (v == t) return true;
            }
        }
    }
    return false;
}

int dfs(int u, int t, int limit)
{
    if (!limit || u == t) return limit;
    int flow = 0, f;
    for (int i = head[u]; i != -1; i = edge[i].nxt) {
        int v = edge[i].to;
        if (dep[v] == dep[u] + 1 && edge[i].val) {
            f = dfs(v, t, min(limit, edge[i].val));
            if (!f) dep[v] = 0;
            flow += f;
            limit -= f;
            edge[i].val -= f;
            edge[i ^ 1].val += f;
            if (!limit) break;
        }
    }
    return flow;
}

void dinic(int s, int t)
{
    while (bfs(s, t))
        maxflow += dfs(s, t, INF);
}

int main()
{
    memset(head, -1, sizeof(head));
    int x, y;
    read(n), read(m), read(e);
    s = n + m + 2, t = n + m + 3;
    for (int i = 1; i <= e; i++) {
        read(x), read(y);
        if (x > n || y > m) continue;
        add(x, y + n, 1);
        add(y + n, x, 0);
    }
    for (int i = 1; i <= n; i++)
        add(s, i, 1), add(i, s, 0);
    for (int i = 1; i <= m; i++)
        add(i + n, t, 1), add(t, i + n, 0);
    dinic(s, t);
    printf("%d\n", maxflow);
    return 0;
}

时间复杂度:\(O(n\sqrt m)\)

四·动态规划

最长上升子序列LIS

const int INF = 1e9;
int d[100005];

void solve() {
    d[0] = -INF;
    for (int i = 1; i <= n; i++) d[i] = INF;
    int ans = 0;
    for (int i = 1; i <= n; i++) {
        int pos = std::lower_boud(d, d + i, a[i]) - d;
        d[pos] = std::min(d[pos], a[i]);
        ans = std::max(ans, pos);
    }
}

时间复杂度:\(O(nlogn)\)

最长公共子序列LCS

const int INF = 1e9;
int a[1001], b[1001], f[1001][1001];

void solve() {
    for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
    for (int i = 1; i <= n; i++) scanf("%d", &b[i]);
    for (int i = 1; i <= n; i++)
        for (int j = 1; j <= n; j++) {
            if (a[i] == b[j]) f[i][j] = max(f[i][j], f[i - 1][j - 1] + 1);
            else f[i][j] = max(f[i - 1][j], f[i][j - 1]);
        }
    ans : f[n][n]
}

时间复杂度:\(O(n^2)\)

背包类问题

01背包

const int INF = 1e9;
int w[1001], v[1001], f[10001];

void solve() {
    for (int i = 1; i <= n; i++) 
        scanf("%d%d", &w[i], &v[i]);
    for (int i = 1; i <= n; i++)
        for (int j = V; j >= w[i]; j--)
            f[i] = std::max(f[i], f[j - w[i]] + v[i]);
}

时间复杂度:\(O(nV)\)

完全背包

const int INF = 1e9;
int w[1001], v[1001], f[10001];

void solve() {
    for (int i = 1; i <= n; i++) 
        scanf("%d%d", &w[i], &v[i]);
    for (int i = 1; i <= n; i++)
        for (int j = w[i]; j <= V; j++)
            f[i] = std::max(f[i], f[j - w[i]] + v[i]);
}

二进制优化多重背包

const int INF = 1e9;
int w, v, k, f[10001];

void solve() {
    while (n--) {
        scanf("%d%d%d", &w, &v, &k);
        for (int d = 1; d < k; k -= d; d <<= 1) 
            for (int i = V; i >= d * w; i--)
                f[i] = std::max(f[i], f[i - w * d] + d * v);
        for (int i = V; i >= k * w; k--)
            f[i] = std::max(f[i], f[i - w * k] + k * v);
    }
}

时间复杂度:\(O(nVlogK)\)

二维费用背包

#include <iostream>
#include <cstdio>
#include <cstring>

int f[1007][1007], a[1007], b[1007], c[1007], n, m, x;

int main()
{
    ios::sync_with_stdio(0);
    cin >> n >> m >> x;
    for (int i = 1; i <= n; i++) cin >> a[i] >> b[i] >> c[i];
    for (int i = 1; i <= n; i++)
        for (int j = m; j >= b[i]; j--)
            for (int v = x; v >= c[i]; v--)
            f[j][v] = std::max(f[j][v], f[j - b[i]][v - c[i]] + a[i]);
    cout << f[m][x] << '\n';        
    return 0;
}

时间复杂度:\(O(nmx)\)

树形动规问题

【JSOI2018】潜入行动

#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define rint register int
#define LL long long
 
const int maxn = 1e5 + 5;
const int MOD = 1e9 + 7;
int n, k, tot, head[maxn], f[maxn][105][4], siz[maxn], g[105][4];

/*
0 -> 安装且被监听
1 -> 安装且不被监听
2 -> 不安装被监听
3 -> 不安装不被监听
*/

struct Edge {
	int to, nxt;
	Edge(int _to, int _nxt) {
		this -> to = _to;
		this -> nxt = _nxt;
	} Edge(){}
}edge[maxn << 1];

void add(int from, int to) {edge[++tot] = Edge(to, head[from]), head[from] = tot;}

template<class T>
inline T read(T &x) {
	x = 0; int w = 1, ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
	return x *= w;
} 

void dfs(int u, int fa) {
	f[u][1][1] = 1; f[u][0][3] = 1; siz[u] = 1;
	for (rint i = head[u]; i; i = edge[i].nxt) {
		int v = edge[i].to; 
		if (v == fa) continue;
		dfs(v, u);
		siz[u] += siz[v]; memcpy(g, f[u], sizeof(g));
		for (rint j = min(siz[u], k); ~j; j--) f[u][j][0] = f[u][j][1] = f[u][j][2] = f[u][j][3] = 0;
		for (rint j = min(siz[u], k); ~j; j--) {
			for (rint p = max(0, j + siz[v] - siz[u]); p <= min(siz[v], j); p++) {
				f[u][j][3] += ((LL)f[v][p][2] * g[j - p][3]) % MOD, f[u][j][3] %= MOD;
				f[u][j][2] += ((LL)(f[v][p][0] % MOD + f[v][p][2] % MOD) % MOD) *
							  ((g[j - p][2] % MOD + g[j - p][3] % MOD) % MOD) % MOD; 
				f[u][j][2] %= MOD;
				f[u][j][1] += ((LL)(f[v][p][2] % MOD + f[v][p][3] % MOD)) % MOD * 
							  g[j - p][1] % MOD; f[u][j][1] %= MOD;
				f[u][j][0] += ((LL)(f[v][p][0] + f[v][p][1] % MOD) + (f[v][p][2] + f[v][p][3]) % MOD)
				 			  % MOD * ((g[j - p][1] % MOD+ g[j - p][0] % MOD)) % MOD; f[u][j][0] %= MOD;
			}
			f[u][j][0] = (f[u][j][0] - f[u][j][1] + MOD) % MOD; 
			f[u][j][2] = (f[u][j][2] - f[u][j][3] + MOD) % MOD; 	
		}
	}
}

int main() {
	int x, y;
	read(n), read(k);
	for (int i = 1; i < n; i++) {
		read(x), read(y);
		add(x, y), add(y, x);
	}
	dfs(1, 0);
	std::cout << f[1][k][0] + f[1][k][2]<< '\n';
	return 0;
}

时间复杂度:树形动规一般情况下为\(O(n)\),树形背包为严格\(O(n^2)\)

数位DP

烦人的数学作业

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long

const int maxn = 25;
const int MOD = 1e9 + 7;
int T, L, R;
int val[maxn], f[maxn][maxn];

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
    return x *= w;
}

int dfs(int pos, int limit, int lead_zero, int k, int sum) {
    if (!pos) return sum;
    if (!limit && !lead_zero && f[pos][sum] != -1)
        return f[pos][sum];
    int lim = limit ? val[pos] : 9;
    int ans = 0;
    for (int i = 0; i <= lim; i++) {
        if (lead_zero && !i)
            ans += dfs(pos - 1, limit && (i == lim), 1, k, sum);
        else
            ans += dfs(pos - 1, limit && (i == lim), 0, k, sum + (i == k));
    }
    if (!limit && !lead_zero)
        f[pos][sum] = ans;
    return ans;
}

int solve(int n, int k) {
    memset(f, -1, sizeof(f));
    int len = 0;
    while (n) val[++len] = n % 10, n /= 10;
    return dfs(len, 1, 1, k, 0);
}

signed main() {
    read(T);
    while (T--) {
        int ans = 0;
        read(L), read(R);
        for (int i = 1; i <= 9; i++)
            ans += (((solve(R, i) - solve(L - 1, i) + MOD) % MOD) * i % MOD + MOD) % MOD, ans %= MOD;
        printf("%lld\n", ans);
    }
    return 0;
}

时间复杂度:\(O(\)玄学不会证\()\)

状态压缩DP

UVA11008 【Antimatter Ray Clearcutting】

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;

const int maxn = 70000; 
int T, n, m, x[20], y[20], g[20][20]; //x,y储存坐标,g用于储存直线
int f[maxn], N;
// f用于记忆化,N为初始状态。
void init()
{
    memset(f, -1, sizeof(f));
    memset(g, 0, sizeof(g));  //多测不清空,爆零两行泪!!!
    scanf("%d%d", &n,   &m);
    N = (1 << n) - 1;  //搜索初始状态
    for (int i = 0; i < n; i++)
        scanf("%d%d", &x[i], &y[i]);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            if (i == j) continue; 
            for (int k = n - 1; k >= 0; k--) {
                g[i][j] <<= 1; //左移一位,为下一个点腾出位置
                if ((x[j] - x[i]) * (y[k] - y[j]) == (y[j] - y[i]) * (x[k] - x[j]))
                    g[i][j]++; //表示这个点在这条直线上
            }
        }
    }
}

int Count(int k)
{
    int cnt = 0;
    for (int i = 0; i < n; i++)
        if ((1 << i) & k) cnt++;
    return cnt;
}

int dfs(int now)
{
    int cnt = Count(now); 
    // Count用于计算还有多少个点没删
    int& ans = f[now]; 
    // 这个取地址符一定要加!! 原因是后面f[now]也要改变
    if (cnt <= n - m) return ans = 0; 
    // 如果剩下的点小于n-m,那么就不用再删了,直接返回0
    else if (cnt == 1) return ans = 1;
    //如果还剩一个点,直接用一条直线来删
    else if (ans > -1) return ans;
    //记忆化,不解释
    ans = (1 << 30);
    //都没有,枚举直线更新状态
    for (int i = 0; i < n; i++) {
        if ((1 << i) & now) {
            // 保证i这个点还没有被删
            for (int j = i + 1; j < n; j++) {
                if ((1 << j) & now) {
                    //保证j这个点没被删
                    int temp = now & (g[i][j] ^ N); //异或取补集,再取and,得到下一个状态
                    ans = min(ans, dfs(temp) + 1); //更新ans
                }
            }
        }
    }
    return ans;
}

int main()
{
    int t = 1;
    scanf("%d", &T);
    while (T--) {
        init(); //初始化
        printf("Case #%d:\n%d\n", t++, dfs(N));
        if (T) puts("");
    }
    return 0;
}

时间复杂度:一般为指数级貌似是废话

五·数据结构

线段树

用处:不说了,大家都知道……

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))

const int maxn = 1e5 + 5;
int n, m, a[maxn], opt, l, r, v;

struct Node {
	int sum, tag;
}z[maxn << 2];

void build(int rt, int l, int r) {
	if (l == r) {
		z[rt].sum = a[l];
		return ;
	}
	int mid = (l + r) >> 1;
	build(rt << 1, l, mid);
	build(rt << 1 | 1, mid + 1, r);
	z[rt].sum = z[rt << 1].sum + z[rt << 1 | 1].sum;
}

void modify(int rt, int l, int r, int x, int y, int v) {
	z[rt].sum += (min(r, y) - max(l, x) + 1) * v;
	if (x <= l && r <= y) {
		z[rt].tag += v;
		return ;
	}
	int mid = (l + r) >> 1;
	if (x <= mid) modify(rt << 1, l, mid, x, y, v);
	if (y > mid) modify(rt << 1 | 1, mid + 1, r, x, y, v);
}

int query(int rt, int l, int r, int x, int y, int tg) {
	if (x <= l && r <= y) {
		return z[rt].sum + (min(r, y) - max(l, x) + 1) * tg;
	}
	int mid = (l + r) >> 1;
	int ret = 0;
	if (x <= mid) ret += query(rt << 1, l, mid, x, y, tg + z[rt].tag);
	if (y > mid) ret += query(rt << 1 | 1, mid + 1, r, x, y, tg + z[rt].tag); 
	return ret;
}

signed main() {
	scanf("%lld%lld", &n, &m);
	for (int i = 1; i <= n; i++) scanf("%lld", &a[i]);
	build(1, 1, n);
	for (int i = 1; i <= m; i++) {
		scanf("%lld", &opt);
		if (opt == 1) {
			scanf("%lld%lld%lld", &l, &r, &v);
			modify(1, 1, n, l, r, v);
		}
		else {
			scanf("%lld%lld", &l, &r);
			printf("%lld\n", query(1, 1, n, l, r, 0));
		}
	}	
	return 0;
}

时间复杂度:\(O(nlogn)\)

树状数组

用处:大家都知道……

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long
#define lowbit(x) ((x) & -(x))

const int maxn = 5e5 + 5;
int n, m;

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x *= w;
}

struct FenwickTree {
    int bit[maxn];
    void add(int x, int v) {for (; x <= n; x += lowbit(x)) bit[x] += v;}
    int query(int x) {int ret = 0; for (; x; x -= lowbit(x)) ret += bit[x]; return ret;}
}T;

signed main() {
    read(n), read(m);
    for (int i = 1; i <= n; i++) {
        int temp; read(temp);
        T.add(i, temp);
    }
    int opt, x, y;
    for (; m; m--) {
        read(opt), read(x), read(y);
        if (opt == 1) T.add(x, y);
        else printf("%lld\n", T.query(y) - T.query(x - 1));
    }
    return 0;
}

时间复杂度:\(O(nlogn)\)

ST表

用处:………………

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>

const int maxn = 1e6 + 5;
int n, m, f[maxn][22];

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) scanf("%d", &f[i][0]);
    for (int j = 1; (1 << j) <= n; j++)
        for (int i = n; i - (1 << j) + 1 >= 1; i--)
            f[i][j] = std::max(f[i][j - 1], f[i - (1 << (j - 1))][j - 1]);
    for (; m; m--) {
        int l, r;
        scanf("%d%d", &l, &r);
        int j = log2(r - l + 1);
        int ans = std::max(f[r][j], f[l + (1 << j) - 1][j]);
        printf("%d\n", ans);
    }
    return 0;
}

时间复杂度:预处理\(O(nlogn)\),查询\(O(1)\)

主席树

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

const int maxn = 2e5 + 5;
int n, m, a[maxn], b[maxn];

struct Node {int l, r, sum; Node(){l = r = sum = 0;}}z[maxn << 5];
int cnt, root[maxn];

template<class T>
inline T read(T &x) {
    x = 0; int w = 1, ch = getchar();
    for (; ch < '0' || ch > '9'; ch = getchar()) if (ch == '-') w = -1;
    for (; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - 48;
    return x *= w;
}

void build(int &rt, int l, int r) {
    rt = ++cnt; if (l == r) return ;
    int mid = (l + r) >> 1;
    build(z[rt].l, l, mid), build(z[rt].r, mid + 1, r);
}

void modify(int &rt, int pre, int l, int r, int pos) {
    rt = ++cnt;
    z[rt].l = z[pre].l, z[rt].r = z[pre].r, z[rt].sum = z[pre].sum + 1;
    if (l == r) return ; int mid = (l + r) >> 1;
    if (pos <= mid) modify(z[rt].l, z[pre].l, l, mid, pos);
    else modify(z[rt].r, z[pre].r, mid + 1, r, pos);
}

int query(int L, int R, int l, int r, int pos) {
    if (l == r) return l; int mid = (l + r) >> 1;
    int temp = z[z[R].l].sum - z[z[L].l].sum;
    if (pos <= temp) return query(z[L].l, z[R].l, l, mid, pos);
    else return query(z[L].r, z[R].r, mid + 1, r, pos - temp);
}

int main() {
    read(n), read(m); int x, y, k;
    for (int i = 1; i <= n; i++) b[i] = read(a[i]);
    std::sort(b + 1, b + n + 1);
    int len = std::unique(b + 1, b + n + 1) - b - 1;
    build(root[0], 1, len);
    for (int i = 1; i <= n; i++) {
        int pos = std::lower_bound(b + 1, b + len + 1, a[i]) - b;
        modify(root[i], root[i - 1], 1, len, pos);
    }
    for (int i = 1; i <= m; i++) {
        read(x), read(y), read(k);
        printf("%d\n", b[query(root[x - 1], root[y], 1, len, k)]);
    }
    return 0;
}

时间复杂度:\(O(nlogn)\)

FHQ Treap

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#define pii pair<int, int>

using namespace std;

const int maxn = 1e5 + 5;
int w, ch;

struct Node {
	int key, val;
	int ls, rs, size;
}g[maxn];
int root, cnt;

template<class T>
T read(T &x)
{
	x = 0, w = 1, ch = getchar();
	while (ch < '0' || ch > '9') {if (ch == '-') w = -1; ch = getchar();}
	while (ch >= '0' && ch <= '9') {x = x * 10 + ch - 48; ch = getchar();}
	return x *= w;
}

void pushup(int p)
{
	g[p].size = g[g[p].ls].size + g[g[p].rs].size + 1;
}

void insert(int x)
{
    ++cnt;
	g[cnt].val = x;
	g[cnt].size = 1;
	g[cnt].key = rand();
	g[cnt].ls = g[cnt].rs = 0;
}

int merge(int x, int y)
{
	if (x == 0) return y;
	if (y == 0) return x;
	if (g[x].key < g[y].key) {
		g[x].rs = merge(g[x].rs, y);
		pushup(x);
		return x;
	}
	else {
		g[y].ls = merge(x, g[y].ls);
		pushup(y);
		return y;
	}
}

void split(int u, int x, int &l, int &r)
{
	if (!u) {l = r = 0; return;}
	if (g[u].val <= x) {l = u; split(g[u].rs, x ,g[u].rs, r);}
	else {r = u; split(g[u].ls, x, l, g[u].ls);}
	pushup(u);
}

int rnk(int u, int num){
    if (num <= g[g[u].ls].size) {
    	int ret = rnk(g[u].ls, num);
    	return ret;
	}	
    else{
        if (num == g[g[u].ls].size + 1) return u;
        else {
            num -= g[g[u].ls].size + 1;
            return rnk(g[u].rs, num);
        }
    }
}

int main()
{
	int n, temp, x, l, r;
	read(n);
	for (int i = 1; i <= n; i++) {
		read(temp), read(x);
		switch (temp) {
			case 1:
				split(root, x, l, r);
				insert(x);
				root = merge(merge(l, cnt), r);
				break;
			case 2:
				int temp;
				split(root, x, l, temp);
				split(l, x - 1, l, r);;
				r = merge(g[r].ls, g[r].rs);
				root = merge(merge(l, r), temp);
				break;
			case 3:
				split(root, x - 1, l, r);
				printf("%d\n", g[l].size + 1);
				root = merge(l, r);
				break;
			case 4:
				printf("%d\n", g[rnk(root, x)].val);
				break;
			case 5:
				split(root, x - 1, l, r);
				printf("%d\n", g[rnk(l, g[l].size)].val);
				root = merge(l, r);
				break;
			case 6:
				split(root, x, l, r);
				printf("%d\n", g[rnk(r, 1)].val);
				root = merge(l, r);
				break;
		}
	}
	return 0;
}

时间复杂度:\(O(nlogn)\)

六·字符串算法

KMP字符串匹配算法

#include <iostream>
#include <cstdio>
#include <cstring>

const int maxn = 1e6 + 6;
int kmp[maxn], j;
char a[maxn], b[maxn];

int main() {
    scanf("%s", a + 1); scanf("%s", b + 1);
    int lena = strlen(a + 1), lenb = strlen(b + 1);
    for (int i = 2; i <= lenb; i++) {
        while (j && b[i] != b[j + 1])
            j = kmp[j];
        if (b[i] == b[j + 1]) j++;
        kmp[i] = j;
    }
    j = 0;
    for (int i = 1; i <= lena; i++) {
        while (j && a[i] != b[j + 1])
            j = kmp[j];
        if (a[i] == b[j + 1]) j++;
        if (j == lenb) {printf("%d\n", i - lenb + 1); j = kmp[j];}
    }
    for (int i = 1; i <= lenb; i++) printf("%d ", kmp[i]); puts("");
    return 0;
}

时间复杂度:\(O(n+m)\)

Manacher算法

#include <iostream>
#include <cstdio>
#include <cstring>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))

using std::string;
const int maxn = 11000002;
string x, s;
int radius[maxn << 1], max_right, mid, ans;

int main() {
    std::cin >> x;
    s += '&', s += '$';
    for (int i = 0; i < x.length(); i++) s += x[i], s += '$';
    for (int i = 1; i < s.length(); i++) {
        radius[i] = max_right > i ? min(radius[mid * 2 - i], max_right - i) : 1;
        while (s[i - radius[i]] == s[i + radius[i]]) radius[i]++;
        if (i + radius[i] > max_right) max_right = i + radius[i], mid = i;
        ans = max(ans, radius[i] - 1);
    }
    printf("%d\n", ans);
    return 0;
}

时间复杂度:\(O(n)\)

Trie树

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long

const int maxn = 5e5 + 5;
int n, m;

struct Trie {
    int ch[maxn][26], siz;
    bool vis[maxn];

    Trie() {
        siz = 1;
        memset(ch[0], 0, sizeof(ch[0]));
        memset(vis, false, sizeof(vis));
    }

    void insert(char *s) {
        int root = 0, len = strlen(s + 1);
        for (int i = 1; i <= len; i++) {
            int ooo = s[i] - 'a';
            if (!ch[root][ooo]) {
                memset(ch[siz], 0, sizeof(ch[siz]));
                ch[root][ooo] = siz++;
            }
            root = ch[root][ooo];
        }
    }

    int search(char *s) {
        int root = 0, len = strlen(s + 1);
        for (int i = 1; i <= len; i++) {
            int ooo = s[i] - 'a';
            if (!ch[root][ooo]) return 0;
            root = ch[root][ooo];
        }
        if (!vis[root]) {
            vis[root] = true;
            return 1;
        }
        return 2;
    }
}trie;

signed main() {
    scanf("%lld", &n);
    char s[100];
    for (int i = 1; i <= n; i++) {
        scanf("%s", s + 1);
        trie.insert(s);
    }
    scanf("%lld", &m);
    for (int i = 1; i <= m; i++) {
        scanf("%s", s + 1);
        int ankh = trie.search(s);
        if (ankh == 0) puts("WRONG");
        else if (ankh == 1) puts("OK");
        else puts("REPEAT");
    }
    return 0;
}

总时间复杂度:\(O(n * len)\)

七·其它

朴素高精度

#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>

using namespace std;
const int maxn = 2005;

struct BigNum {
    int d[maxn], len;

    void clean() {while (len > 1 && !d[len - 1]) len--;}
    BigNum() {memset(d, 0, sizeof(d)); len = 1;}
    BigNum(int num) {*this = num;}
    BigNum(char *num) {*this = num;}

    BigNum operator = (const char *num) {
        memset(d, 0, sizeof(d)), len = strlen(num);
        for (int i = 0; i < len; i++) d[i] = num[len - i - 1] - '0';
        clean();
        return *this;
    }
    BigNum operator = (int num) {
        char s[20]; sprintf(s, "%d", num);
        *this = s;
        return *this;
    }

    BigNum operator + (const BigNum &b) {
        BigNum c = *this; int i;
        for (i = 0; i < b.len; i++) {
            c.d[i] += b.d[i];
            if (c.d[i] > 9) c.d[i] %= 10, c.d[i + 1]++;
        }
        while (c.d[i] > 9) c.d[i++] %= 10, c.d[i]++;
        c.len = max(len, b.len);
        if (c.d[i] && c.len <= i) c.len = i + 1;
        return c;
    }

    BigNum operator - (const BigNum &b) {
        BigNum c = *this; int i;
        for (i = 0 ; i < b.len; i++) {
            c.d[i] -= b.d[i];
            if (c.d[i] < 0) c.d[i] += 10, c.d[i + 1]--;
        }
        while (c.d[i] < 0) c.d[i++] += 10, c.d[i]--;
        c.clean();
        return c;
    }

    BigNum operator * (const BigNum &b) const {
        BigNum c; int i, j; c.len = (len + b.len);
        for (j = 0 ; j < b.len; j++)
            for (i = 0; i < len; i++)
                c.d[i + j] = d[i] * b.d[j];
        for (i = 0; i < c.len - 1; i++)
            c.d[i + 1] += c.d[i] / 10, c.d[i] %= 10;
        c.clean();
        return c;
    }

    BigNum operator / (const BigNum &b) {
        int i, j;
        BigNum c = *this, last = 0;
        for (i = len - 1; i >= 0; i--) {
            last = last * 10 + d[i];
            for (j = 0 ; j < 10; j++) if (last < b * (j + 1)) break;
            c.d[i] = j;
            last = last - b * j;
        }
        c.clean();
        return c;
    }

    BigNum operator % (const BigNum &b) {
        int i, j;
        BigNum last = 0;
        for (i = len - 1; i >= 0; i--) {
            last = last * 10 + d[i];
            for (j = 0; j < 10; j++) if (last < b * (j + 1)) break;
            last = last - b * j;
        }
        return last;
    }

    bool operator < (const BigNum &b) const {
		if (len != b.len) return len < b.len;
		for (int i = len - 1; i >= 0; i--)
			if (d[i] != b.d[i]) return d[i] < b.d[i];
		return false;
	}

    string str() const {
        char s[maxn] = {};
        for (int i = 0; i < len; i++) s[len - i - 1] = d[i] + '0';
        return s;
    }
};

istream& operator >> (istream& in, BigNum &x) {
    string s;
    in >> s;
    x = s.c_str();
    return in;
}

ostream& operator << (ostream& out, const BigNum &x) {
    out << x.str();
    return out;
}

int main() {
    BigNum a, b;
    cin >> a >> b;
    cout << a + b << '\n';
    return 0;
}

压位高精度

// 受rainy大佬所教

const int_t base = 100000000;

void print(int_t x, int_t dig) {
	if(!dig) return;
	print(x / 10, dig - 1);
	putchar(x % 10 + '0');
}

sturct BigInt {
	int_t num[10], siz;
	BigInt():siz(0){memset(num, 0, sizeof num);}
	void operator !(){for(int_t i=0;i<siz;i++)if(num[i]>=base)siz=max(siz,i+2),num[i+1]+=num[i]/base,num[i]%=base;}
	void operator +=(int_t x){num[0]+=x;!*this;}
	void operator *=(int_t x){for(int_t i=0;i<siz;i++)num[i]*=x;!*this;}
	void operator +=(BigInt x){siz=x.siz=max(siz, x.siz);for(int_t i=0;i<siz;i++)num[i]+=x[i];!*this;}
	void operator ~(){printf("%lld",num[siz-1]);for(int_t i=siz-2;~i;i--)print(num[i],8);}
};

BigInt read() {
	BigInt ans;
	while(!isdigit(ch)) ch = getchar();
	while(isdigit(ch)) ans *= 10, ans += ch - '0', ch = getch();
	return ans;
}

int main() {
	BigInt a; a += 100000000; 
	a *= 123123123;
	~a;
}

矩阵快速幂加速递推

#include <iostream>
#include <cstdio>
#include <cstring>
#define int long long

using namespace std;

const int MOD = 1e9 + 7;
int n;

struct Matrix {
    int a[4][4];
    Matrix(){memset(a, 0, sizeof(a));}
}ans, base;

Matrix operator * (const Matrix &x, const Matrix &y)
{
    Matrix ret;
    for (int i = 1; i <= 2; i++)
        for (int j = 1; j <= 2; j++)
            for (int k = 1; k <= 2; k++)
                ret.a[i][j] = (ret.a[i][j] + x.a[i][k] * y.a[k][j]) % MOD;
    return ret;
}

void init()
{
    base.a[1][1] = base.a[1][2] = base.a[2][1] = 1;
    base.a[2][2] = 0;
    ans.a[1][1] = ans.a[1][2] = 1;
}

void quick_pow(int b)
{
    while (b) {
        if (b & 1) ans = ans * base;
        base = base * base;
        b >>= 1;
    }
}

signed main()
{
    init();
    scanf("%lld", &n);
    if (n <= 2) {puts("1"); return 0;}
    quick_pow(n - 2);
    printf("%lld\n", ans.a[1][1]);
    return 0;
}

时间复杂度:快速幂\(O(logn)\),根据矩阵大小不同会带不同大小的常数

大模拟

猪国杀为例

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <queue>
#define loves =

using std::cin;
using std::cout;
using std::vector;
using std::queue;
using std::string;
int n, m; // n -> 玩家数目 m -> 牌堆牌的数量
int sf[12]; // 身份
int lsf[12]; // 身份确定前的类身份
int health[12]; // 血量
bool crossbow[12]; // 猪哥连弩
bool died[12]; // 是否已死
vector<char>shoupai[12]; // 每只猪手牌
queue<char>heap; //牌堆

// ============card部分============
char get_card() { // 牌堆里抽卡
    char ch = heap.front();
    heap.pop();
    if (heap.empty()) heap.push(ch);
    return ch;
}

void Erase_card(int rt, char ch) {
    for (auto it = shoupai[rt].begin(); it != shoupai[rt].end(); it++)
        if (*it == ch) {
            shoupai[rt].erase(it);
            return ;
        }
}

bool has_card(int rt, char ch) {
    for (auto it : shoupai[rt])
        if (it == ch) return true;
    return false;
}
// ============card部分============

// ============读入部分============
void input() {
    cin >> n >> m;
    for (int i =1 ; i <= n; i++) {
        string temp;
        cin >> temp; health[i] = 4;
        if (temp[0] == 'M' || temp[0] == 'Z') sf[i] = 1;
        else sf[i] = 2;
        for (int j = 1; j <= 4; j++) {
            char ch;
            cin >> ch;
            shoupai[i].push_back(ch);
        }
    }
    while (m--) {
        char ch;
        cin >> ch;
        heap.push(ch);
    }
}
// ============读入部分============

// ============输出部分============
void output() {
    int num_good = 0, num_bad = 0;
    for (int i = 1; i <= n; i++)
        if (!died[i])
            if (sf[i] == 1)
                num_good++;
            else num_bad++;
    if (num_good && !died[1]) puts("MP");
    else puts("FP");
    for (int i = 1; i <= n; i++) {
        if (died[i]) puts("DEAD");
        else {
            for (auto it : shoupai[i]) cout << it << ' ';
            puts("");
        }
    }
    exit(0);
}
// ============输出部分============

// ============无懈可击============
bool can_WX(int fr, int rt, bool isWX) {
    // fr used WX for rt
    int nxt = fr;
    do {
        if (!died[nxt] && (isWX ? sf[nxt] != lsf[rt] : sf[nxt] == lsf[rt]) && has_card(nxt, 'J')) {
            Erase_card(nxt, 'J');
            lsf[nxt] = sf[nxt];
            if (can_WX(nxt, nxt, true))
                return false;
            else return true;
        }
        ++nxt;
        if (nxt == n + 1) nxt = 1;
    } while (nxt != fr);
    return false;
}
// ============无懈可击============

// ============掉血============
void be_heart(int fr, int rt, bool jinnang) {
    health[rt]--;
    //std::cout << rt << "heart by" << fr << '\n';
    if (rt == 1 && lsf[fr] == 0)
        lsf[fr] = 3;
    if (!jinnang && lsf[fr] && lsf[fr] != 3)
        lsf[fr] = sf[fr];
    if (health[rt]) return ;
    if (!health[rt] && has_card(rt, 'P')) {
        Erase_card(rt, 'P');
        ++health[rt];
    }
    else {
        died[rt] = true;
        if (rt == 1) output();
        int num_bad = 0;
        for (int i =1 ; i <= n; i++)
            if (!died[i])
                if (sf[i] == 2) num_bad++;
        if (num_bad == 0) output();
        else {
            if (fr == 1 && sf[rt] == 1) {
                shoupai[1].clear();
                crossbow[1] = false;
            }
            if (sf[rt] == 2) {
                shoupai[fr].push_back(get_card());
                shoupai[fr].push_back(get_card());
                shoupai[fr].push_back(get_card());
            }
        }
    }
}
// ============掉血============

// ============南猪入侵============
void use_NM(int rt) {
    int nxt = rt + 1;
    if (nxt == n + 1) nxt = 1;
    while (nxt != rt) {
        if (!died[nxt]) {
            if (can_WX(rt, nxt, false)) {
                ++nxt;
                if (nxt == n + 1) nxt = 1;
                continue;
            }
            if (has_card(nxt, 'K'))
                Erase_card(nxt, 'K');
            else {
                be_heart(rt, nxt, true);
                //std::cout << rt << "uesd NM hit" << nxt << "heart 1 滴血" << '\n';
            }
        }
        ++nxt;
        if (nxt == n + 1) nxt = 1;
    }
}
// ============南猪入侵============

// ============万箭齐发============
void use_WJ(int rt) {
    int nxt = rt + 1;
    if (nxt == n + 1) nxt = 1;
    while (nxt != rt) {
        if (!died[nxt]) {
            if (can_WX(rt, nxt, false)) {
                ++nxt;
                if (nxt == n + 1) nxt = 1;
                continue;
            }
            if (has_card(nxt, 'D'))
                Erase_card(nxt, 'D');
            else {
                be_heart(rt, nxt, true);
                //std::cout << rt << "uesd WJ hit" << nxt << "heart 1 滴血" << '\n';
            }
        }
        ++nxt;
        if (nxt == n + 1) nxt = 1;
    }
}
// ============万箭齐发============

// ============决斗============
void duel(int fr, int to) {
    if (!has_card(to, 'K') || (fr == 1 && sf[to] == 1)) {be_heart(fr, to, true); return ;}
    else {
        Erase_card(to, 'K');
        duel(to, fr);
    }
}
// ============决斗============

// ============寻找下一个杀或决斗的目标============
int Find(int rt, bool limit) {
    if (!limit && sf[rt] == 2) return 1;
    int nxt = rt + 1;
    if (nxt == n + 1) nxt = 1;
    while (nxt != rt) {
        //std::cout << "now find mubiao:" << nxt << '\n';
        if (died[nxt]) {
            ++nxt;
            if (nxt == n + 1) nxt = 1;
            continue;
        }
        bool canhit = (rt == 1 ? lsf[nxt] >= 2 : lsf[nxt] != sf[rt]);
        if (rt > 1) {
            if (lsf[nxt] == 0 || lsf[nxt] == 3) canhit = false;
            else canhit = (lsf[nxt] != sf[rt]);
        }
        if (canhit) return nxt;
        else if (limit) return 0;
        ++nxt;
        if (nxt == n + 1) nxt = 1;
    }
    return 0;
}
// ============寻找下一个杀或决斗的目标============

// ============游戏过程============
void solve() {
    lsf[1] = 1;
     int rt = 0;
     while (true) {
        ++rt;
        if (rt == n + 1) rt = 1;
        if (died[rt]) continue;
        shoupai[rt].push_back(get_card());
        shoupai[rt].push_back(get_card());
        bool usedk = false;
        while (true) {
            if (died[rt]) break;
            bool usedcard = false;
            for (auto i = shoupai[rt].begin(); i != shoupai[rt].end(); i++) {
                char it = *i;
                if (it == 'P') {
                    if (health[rt] != 4) {
                        ++health[rt];
                        Erase_card(rt, 'P');
                        usedcard = true;
                        break;
                    }
                }
                else if (it == 'N'){
                    Erase_card(rt, 'N');
                    use_NM(rt);
                    usedcard = true;
                    break;
                }
                else if (it == 'W'){
                    Erase_card(rt, 'W');
                    use_WJ(rt);
                    usedcard = true;
                    break;
                }
                else if (it == 'Z') {
                    crossbow[rt] = true;
                    Erase_card(rt, 'Z');
                    usedcard = true;
                    break;
                }
                else if (it == 'K') {
                    if (usedk && !crossbow[rt]) continue;
                    int to = Find(rt, true);
                    if (!to) continue;
                    Erase_card(rt, 'K');
                    usedcard = usedk = true;
                    if (has_card(to, 'D')) {Erase_card(to, 'D'); ++health[to];}
                    be_heart(rt, to, false);
                    break;
                }
                else if (it == 'F') {
                    int to = Find(rt, false);
                    if (!to) continue;
                    lsf[rt] = sf[rt];
                    Erase_card(rt, 'F');
                    usedcard = true;
                    if (can_WX(rt, to, false)) break;
                    duel(rt, to);
                    usedcard = true;
                    break;
                }
            }
            if (usedcard) continue;
            break;
        }
     }
}
// ============游戏过程============

int haj() {
    input();
    solve();
    return 0;
}

int ziiidan loves haj();

int main() {}

大概……也许……就这些了吧……

\(CSP-S\ 2019\) \(RP++!!!\)

posted @ 2019-11-11 11:38  Hydrogen_Helium  阅读(225)  评论(1编辑  收藏  举报