快速读入、输出,及其他模板
头
如果你在我博客里,读到某个代码没有头。请把这段复制到代码前面:
// 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
- 建虚树模板