快速读入、输出,及其他模板

如果你在我博客里,读到某个代码没有头。请把这段复制到代码前面:

// problem: 
#include <bits/stdc++.h>
using namespace std;

#define pb push_back
#define mk make_pair
#define lob lower_bound
#define upb upper_bound
#define fi first
#define se second
#define SZ(x) ((int)(x).size())

typedef unsigned int uint;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

template<typename T> inline void ckmax(T& x, T y) { x = (y > x ? y : x); }
template<typename T> inline void ckmin(T& x, T y) { x = (y < x ? y : x); }

以下所有模板,如无特殊说明,请在使用前加上“头”。示例:

[头]: 放在最前面

[模板]: 放在头后面

[你的代码]: 调用[模板]

快速读入、输出

用法:把这段代码复制到你的代码前面,然后用cin/cout正常写,就会变成快读、快输了。

特别说明

  • 目前不支持小数类(double,long double,float),不支持\(\texttt{C++}\)std :: string(但是支持char数组)。
  • 支持 __int128
  • 请不要用在交互题中
/* --------------- fast io --------------- */ // begin
namespace Fread {
const int SIZE = 1 << 21;
char buf[SIZE], *S, *T;
inline char getchar() {
	if (S == T) {
		T = (S = buf) + fread(buf, 1, SIZE, stdin);
		if (S == T) return '\n';
	}
	return *S++;
}
} // namespace Fread
namespace Fwrite {
const int SIZE = 1 << 21;
char buf[SIZE], *S = buf, *T = buf + SIZE;
inline void flush() {
	fwrite(buf, 1, S - buf, stdout);
	S = buf;
}
inline void putchar(char c) {
	*S++ = c;
	if (S == T) flush();
}
struct NTR {
	~ NTR() { flush(); }
} ztr;
} // namespace Fwrite
#ifdef ONLINE_JUDGE
#define getchar Fread :: getchar
#define putchar Fwrite :: putchar
#endif
namespace Fastio {
struct Reader {
	template<typename T>
	Reader& operator >> (T& x) {
		char c = getchar();
		T f = 1;
		while (c < '0' || c > '9') {
			if (c == '-') f = -1;
			c = getchar();
		}
		x = 0;
		while (c >= '0' && c <= '9') {
			x = x * 10 + (c - '0');
			c = getchar();
		}
		x *= f;
		return *this;
	}
	Reader& operator >> (char& c) {
		c = getchar();
		while (c == ' ' || c == '\n') c = getchar();
		return *this;
	}
	Reader& operator >> (char* str) {
		int len = 0;
		char c = getchar();
		while (c == ' ' || c == '\n') c = getchar();
		while (c != ' ' && c != '\n' && c != '\r') { // \r\n in windows
			str[len++] = c;
			c = getchar();
		}
		str[len] = '\0';
		return *this;
	}
	Reader(){}
} cin;
const char endl = '\n';
struct Writer {
	template<typename T>
	Writer& operator << (T x) {
		if (x == 0) { putchar('0'); return *this; }
		if (x < 0) { putchar('-'); x = -x; }
		static int sta[45];
		int top = 0;
		while (x) { sta[++top] = x % 10; x /= 10; }
		while (top) { putchar(sta[top] + '0'); --top; }
		return *this;
	}
	Writer& operator << (char c) {
		putchar(c);
		return *this;
	}
	Writer& operator << (char* str) {
		int cur = 0;
		while (str[cur]) putchar(str[cur++]);
		return *this;
	}
	Writer& operator << (const char* str) {
		int cur = 0;
		while (str[cur]) putchar(str[cur++]);
		return *this;
	}
	Writer(){}
} cout;
} // namespace Fastio
#define cin Fastio :: cin
#define cout Fastio :: cout
#define endl Fastio :: endl
/* --------------- fast io --------------- */ // end

取模运算模板

适用范围:\(\text{MOD} \times 2 < \text{INT_MAX}\) 时。例如,\(\text{MOD} = 10^9+7\)

支持两个 \([0, \text{MOD})\) 范围内的数,进行加、减法运算。

支持预处理阶乘,需要调用 facinit() 函数,默认预处理的上界为一个常量 MAXN。通过预处理出的阶乘,可以用来计算组合数。

inline int mod1(int x) { return x < MOD ? x : x - MOD; }
inline int mod2(int x) { return x < 0 ? x + MOD : x; }
inline void add(int &x, int y) { x = mod1(x + y); }
inline void sub(int &x, int y) { x = mod2(x - y); }
inline int pow_mod(int x, int i) {
	int y = 1;
	while (i) {
		if (i & 1) y = (ll)y * x % MOD;
		x = (ll)x * x % MOD;
		i >>= 1;
	}
	return y;
}

int fac[MAXN + 5], ifac[MAXN + 5];
inline int comb(int n, int k) {
	if (n < k) return 0;
	return (ll)fac[n] * ifac[k] % MOD * ifac[n - k] % MOD;
}
void facinit(int lim = MAXN) {
	fac[0] = 1;
	for (int i = 1; i <= lim; ++i) fac[i] = (ll)fac[i - 1] * i % MOD;
	ifac[lim] = pow_mod(fac[lim], MOD - 2);
	for (int i = lim - 1; i >= 0; --i) ifac[i] = (ll)ifac[i + 1] * (i + 1) % MOD;
}

快速数论变换(NTT)

  • 本代码仅适用于模 \(998244353\) 意义下的卷积。使用时要在本段代码前面定义一个全局的常量 MOD = 998244353
  • 使用时要在本段代码前面定义一个全局的常量 MAXN,表示要被卷积的序列的最大长度。
  • 使用时请将取模运算模板复制到本代码前。
namespace NTT {
const int SIZE = MAXN * 4;
int rev[SIZE + 5], a[SIZE + 5], b[SIZE + 5];
void NTT(int a[], int n, int flag) {
	for (int i = 0; i < n; ++i) if (i < rev[i]) swap(a[i], a[rev[i]]);
	for (int i = 1; i < n; i <<= 1) {
		int T = pow_mod(3, (MOD - 1) / (i << 1));
		if (flag == -1) T = pow_mod(T, MOD - 2);
		for (int j = 0; j < n; j += (i << 1)) {
			for (int k = 0, t = 1; k < i; ++k, t = (ll)t * T % MOD) {
				int Nx = a[j + k], Ny = (ll)a[i + j + k] * t % MOD;
				a[j + k] = mod1(Nx + Ny);
				a[i + j + k] = mod2(Nx - Ny);
			}
		}
	}
	if (flag == -1) {
		int inv_n = pow_mod(n, MOD - 2);
		for (int i = 0; i < n; ++i) a[i] = (ll)a[i] * inv_n % MOD;
	}
}
void work(const int _a[], const int _b[], int _res[], int n, int m, int reslen) {
	int lim = 1, cnt = 0;
	while (lim <= n + m) lim <<= 1, cnt++;
	for (int i = 1; i < lim; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (cnt - 1));
	
	for (int i = 0; i < n; ++i) a[i] = _a[i];
	for (int i = n; i < lim; ++i) a[i] = 0;
	for (int i = 0; i < m; ++i) b[i] = _b[i];
	for (int i = m; i < lim; ++i) b[i] = 0;
	
	NTT(a, lim, 1);
	NTT(b, lim, 1);
	for (int i = 0; i < lim; ++i) a[i] = (ll)a[i] * b[i] % MOD;
	NTT(a, lim, -1);
	
	for (int i = 0; i < reslen; ++i) _res[i] = a[i];
}
} // namespace NTT

网络流

网络最大流

  • 适用范围:每条边的流量都在 int 类型范围内。
  • 使用前需要先调用 init() 函数。如果需要清空(例如多测时),则在函数里传一个参数 true
namespace Flow {
const ll LL_INF = 1e18;
const int INF = 1e9;
const int MAXN = 1e5;
const int MAXM = 1e7;
struct EDGE { int nxt, to, w; } edge[MAXM + 5];
int head[MAXN + 5], tot;
inline void add_edge(int u, int v, int w) {
	edge[++tot].nxt = head[u], edge[tot].to = v, edge[tot].w = w, head[u] = tot;
	edge[++tot].nxt = head[v], edge[tot].to = u, edge[tot].w = 0, head[v] = tot;
}
int maxnode;
int d[MAXN + 5], cur[MAXN + 5];
bool bfs(int s, int t) {
	for (int i = 1; i <= maxnode; ++i) cur[i] = head[i];
	memset(d, 0, sizeof(int) * (maxnode + 1));
	queue<int> q;
	q.push(s);
	d[s] = 1;
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		for (int i = head[u]; i; i = edge[i].nxt) {
			int v = edge[i].to;
			if (edge[i].w && !d[v]) {
				d[v] = d[u] + 1;
				if (v == t)
					return true;
				q.push(v);
			}
		}
	}
	return false;
}
int dfs(int u, int flow, const int& t) {
	if (u == t)
		return flow;
	int rest = flow;
	for (int& i = cur[u]; i; i = edge[i].nxt) {
		int v = edge[i].to;
		if (edge[i].w && d[v] == d[u] + 1) {
			int k = dfs(v, min(rest, edge[i].w), t);
			if (!k) {
				d[v] = 0;
				continue;
			}
			edge[i].w -= k;
			edge[i ^ 1].w += k;
			rest -= k;
			if (!rest)
				return flow - rest;
		}
	}
	return flow - rest;
}
int maxflow(int s, int t, int _ma = -1) {
	if (_ma == -1) maxnode = t;
	else maxnode = _ma;
	int maxflow = 0, tmp;
	while (bfs(s, t)) {
		while ((tmp = dfs(s, INF, t)) != 0) maxflow += tmp;
	}
	return maxflow;
}
void init(bool _clear = false) {
	tot = 1;
	if (_clear) memset(head, 0, sizeof(int) * (maxnode + 1));
}
} // namespace Flow

最小费用最大流

  • 适用范围:每条边的流量都在 int 类型范围内,费用之和在 long long 类型范围内。
  • 使用前需要先调用 init() 函数。如果需要清空(例如多测时),则在函数里传一个参数 true
namespace MCMF {
const ll LL_INF = 1e18;
const int INF = 1e9;
const int MAXN = 1e5;
const int MAXM = 1e7;
struct EDGE { int nxt, to, w; ll cost; } edge[MAXM + 5];
int head[MAXN + 5], tot;
inline void add_edge(int u, int v, int w, ll c) {
	edge[++tot].nxt = head[u], edge[tot].to = v, edge[tot].w = w, edge[tot].cost = c, head[u] = tot;
	edge[++tot].nxt = head[v], edge[tot].to = u, edge[tot].w = 0, edge[tot].cost = -c, head[v] = tot;
}
int maxnode;
int pre[MAXN + 5];
bool inq[MAXN + 5];
ll dis[MAXN + 5];
bool SPFA(int s, int t) {
	queue<int> q;
	for (int i = 1; i <= maxnode; ++i) {
		pre[i] = 0;
		inq[i] = 0;
		dis[i] = LL_INF;
	}
	dis[s] = 0;
	q.push(s);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		inq[u] = 0;
		for (int i = head[u]; i; i = edge[i].nxt) {
			int v = edge[i].to;
			if (!edge[i].w)
				continue;
			if (dis[u] + edge[i].cost < dis[v]) {
				dis[v] = dis[u] + edge[i].cost;
				pre[v] = i;
				if (!inq[v]) {
					inq[v] = 1;
					q.push(v);
				}
			}
		}
	}
	return pre[t] != 0;
}
pair<int, ll> mcmf(int s, int t, int _ma = -1) {
	if (_ma == -1) maxnode = t;
	else maxnode = _ma;
	int maxflow = 0;
	ll mincost = 0;
	while (SPFA(s, t)) {
		int curflow = INF;
		for (int i = pre[t]; i; i = pre[edge[i ^ 1].to]) {
			ckmin(curflow, edge[i].w);
		}
		maxflow += curflow;
		for (int i = pre[t]; i; i = pre[edge[i ^ 1].to]) {
			mincost += edge[i].cost * curflow;
			edge[i].w -= curflow;
			edge[i ^ 1].w += curflow;
		}
	}
	return mk(maxflow, mincost);
}
void init(bool _clear = false) {
	tot = 1;
	if (_clear) memset(head, 0, sizeof(int) * (maxnode + 1));
}
} // namespace MCMF

树相关模板

链式前向星

  • 使用时要在本段代码前面定义一个全局的常量 MAXN,表示树的最大节点数。
struct EDGE { int nxt, to; } edge[MAXN * 2 + 5];
int head[MAXN + 5], tot;
inline void add_edge(int u, int v) { edge[++tot].nxt = head[u]; edge[tot].to = v; head[u] = tot; }

树链剖分

  • 使用时要在本段代码前面定义一个全局的常量 MAXN,表示树的最大节点数。
  • 链式前向星存树。
int fa[MAXN + 5], sz[MAXN + 5], son[MAXN + 5], dep[MAXN + 5];
void dfs1(int u) {
	sz[u] = 1;
	for (int i = head[u]; i; i = edge[i].nxt) {
		int v = edge[i].to;
		if (v == fa[u])
			continue;
		fa[v] = u;
		dep[v] = dep[u] + 1;
		dfs1(v);
		sz[u] += sz[v];
		if (!son[u] || sz[v] > sz[son[u]])
			son[u] = v;
	}
}
int top[MAXN + 5], dfn[MAXN + 5], ofn[MAXN + 5], rev[MAXN + 5], cnt_dfn;
void dfs2(int u, int t) {
	top[u] = t;
	dfn[u] = ++cnt_dfn;
	rev[cnt_dfn] = u;
	if (son[u])
		dfs2(son[u], t);
	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);
	}
	ofn[u] = cnt_dfn;
}
int get_lca(int u, int v) {
	while (top[u] != top[v]) {
		if (dep[top[u]] < dep[top[v]])
			swap(u, v);
		u = fa[top[u]];
	}
	return (dep[u] < dep[v]) ? u : v;
}

欧拉序LCA+求距离

  • 适用于有边权的树。所有边权之和不应超出 \(\texttt{long long}\) 范围。
  • 设树的节点数为 \(n\),则预处理时间复杂度 \(O(n\log n)\),查询时间复杂度 \(O(1)\)
  • 使用时要在本段代码前面定义两个全局的常量 MAXN, LOG,表示树的最大节点数,和 ceil(log2(MAXN * 2))
  • 链式前向星存树。
ll dis[MAXN + 5];
int dep[MAXN + 5], dfn[MAXN + 5], arr[MAXN * 2 + 5], st[MAXN * 2 + 5][LOG + 1], _log2[MAXN * 2 + 5], cnt;
void dfs(int u, int fa) {
	dep[u] = dep[fa] + 1;
	dfn[u] = ++cnt;
	arr[cnt] = u;
	for (int i = head[u]; i; i = edge[i].nxt) {
		int v = edge[i].to;
		if (v == fa)
			continue;
		dis[v] = dis[u] + edge[i].w;
		dfs(v, u);
		arr[++cnt] = u;
	}
}
inline int get_lca(int u, int v) {
	int l = dfn[u], r = dfn[v];
	if (l > r)
		swap(l, r);
	int k = _log2[r - l + 1];
	return dep[st[l][k]] < dep[st[r - (1 << k) + 1][k]] ? st[l][k] : st[r - (1 << k) + 1][k];
}
inline ll get_dist(int u, int v) {
	if (u == v)
		return 0;
	int lca = get_lca(u, v);
	return dis[u] + dis[v] - dis[lca] * 2;
}
void eulerLCA_init() {
	dfs(1, 0);
	for (int i = 1; i <= cnt; ++i) st[i][0] = arr[i];
	for (int j = 1; j <= LOG; ++j) {
		for (int i = 1; i + (1 << (j - 1)) <= cnt; ++i) {
			st[i][j] = (dep[st[i][j - 1]] < dep[st[i + (1 << (j - 1))][j - 1]] ? st[i][j - 1] : st[i + (1 << (j - 1))][j - 1]);
		}
	}
	_log2[0] = -1;
	for (int i = 1; i <= cnt; ++i) _log2[i] = _log2[i >> 1] + 1;
}

st表与rmq

  • 支持查询一个序列的最大值,及最大值出现位置(有多个位置默认后面的)。设序列长度为 \(n\),则预处理时间复杂度 \(O(n\log n)\),查询时间复杂度 \(O(1)\)
  • 使用时要在本段代码前面定义一个全局的常量 MAXN,表示序列的最大长度。
  • 如果想改成查最小值,只需要在标注 ***** 的地方修改。
struct RangeMaxQuery {
	int _log2[MAXN + 5];
	pii st[MAXN + 5][LOG + 1];
	void build(int* a, int n) {
		_log2[0] = -1;
		for (int i = 1; i <= n; ++i) {
			_log2[i] = _log2[i >> 1] + 1;
			st[i][0] = mk(a[i], i);
		}
		for (int j = 1; j <= LOG; ++j) {
			for (int i = 1; i + (1 << (j - 1)) <= n; ++i) {
				st[i][j] = max(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]); // *****
			}
		}
	}
	pii rmq(int l, int r) {
		int k = _log2[r - l + 1];
		return max(st[l][k], st[r - (1 << k) + 1][k]); // *****
	}
	int rmq_val(int l, int r) { return rmq(l, r).fi; }
	int rmq_pos(int l, int r) { return rmq(l, r).se; }
	RangeMaxQuery() {}
} RMQ;

娱乐向:DJQ DXM TIE TIE

/*
_/_/_/_/    _/_/_/_/_/  _/_/_/
_/      _/      _/    _/      _/
_/      _/      _/    _/      _/
_/      _/      _/    _/      _/
_/      _/      _/    _/  _/  _/
_/      _/  _/  _/    _/    _/_/
_/_/_/_/      _/_/     _/_/_/_/_/

_/_/_/_/    _/    _/  _/      _/
_/      _/   _/  _/   _/_/  _/_/
_/      _/    _/_/    _/ _/_/ _/
_/      _/     _/     _/  _/  _/
_/      _/    _/_/    _/      _/
_/      _/   _/  _/   _/      _/
_/_/_/_/    _/    _/  _/      _/

_/_/_/_/_/ _/_/_/_/_/ _/_/_/_/_/
    _/         _/     _/
    _/         _/     _/
    _/         _/     _/_/_/_/
    _/         _/     _/
    _/         _/     _/
    _/     _/_/_/_/_/ _/_/_/_/_/

_/_/_/_/_/ _/_/_/_/_/ _/_/_/_/_/
    _/         _/     _/
    _/         _/     _/
    _/         _/     _/_/_/_/
    _/         _/     _/
    _/         _/     _/
    _/     _/_/_/_/_/ _/_/_/_/_/
*/

即将更新...

  • 李超树模板
  • 线性基模板
  • KMP / EXKMP 模板
  • SAM / 广义 SAM
  • SA
  • 建虚树模板
posted @ 2020-07-23 07:41  duyiblue  阅读(1711)  评论(3编辑  收藏  举报