动态规划
数位dp
pos 表示当前枚举到第几位
sum 表示 d 出现的次数
limit 为 1 表示枚举的数字有限制
zero 为 1 表示有前导 0
d 表示要计算出现次数的数
LL dfs(int pos, LL sum, int limit, int zero, int d) {
if (pos == 0) return sum;
if (!limit && !zero && dp[pos][sum] != -1) return dp[pos][sum];
LL ans = 0;
int up = (limit ? num[pos] : 9);
for (int i = 0; i <= up; i++) {
ans += dfs(pos - 1, sum + ((!zero || i) && (i == d)), limit && (i == num[pos]), zero && (i == 0), d);
}
if (!limit && !zero) dp[pos][sum] = ans;
return ans;
}
LL solve(LL x, int d) {
memset(dp, -1, sizeof dp);
int len = 0;
while (x) {
num[++len] = x % 10;
x /= 10;
}
return dfs(len, 0, 1, 1, d);
}
数据结构
树
struct Tree {
int n;
vector<vector<pair < int, int>>> e;
vector<int> dep, parent, maxdep, d1, d2, s1, s2, up;
Tree(int n) {
this->n = n;
e.resize(n + 1);
dep.resize(n + 1);
parent.resize(n + 1);
maxdep.resize(n + 1);
d1.resize(n + 1);
d2.resize(n + 1);
s1.resize(n + 1);
s2.resize(n + 1);
up.resize(n + 1);
}
void add(int u, int v, int w) {
e[u].push_back({w, v});
e[v].push_back({w, u});
}
void dfs(int u, int fa) {
maxdep[u] = dep[u];
for (auto [w, v]: e[u]) {
if (v == fa) continue;
dep[v] = dep[u] + 1;
parent[v] = u;
dfs(v, u);
maxdep[u] = max(maxdep[u], maxdep[v]);
}
}
void dfs1(int u, int fa) {
for (auto [w, v]: e[u]) {
if (v == fa) continue;
dfs1(v, u);
int x = d1[v] + w;
if (x > d1[u]) {
d2[u] = d1[u], s2[u] = s1[u];
d1[u] = x, s1[u] = v;
} else if (x > d2[u]) {
d2[u] = x, s2[u] = v;
}
}
}
void dfs2(int u, int fa) {
for (auto [w, v]: e[u]) {
if (v == fa) continue;
if (s1[u] == v) {
up[v] = max(up[u], d2[u]) + w;
} else {
up[v] = max(up[u], d1[u]) + w;
}
dfs2(v, u);
}
}
int radius, center, diam;
void getCenter() {
center = 1; //中心
for (int i = 1; i <= n; i++) {
if (max(d1[i], up[i]) < max(d1[center], up[center])) {
center = i;
}
}
radius = max(d1[center], up[center]); //距离最远点的距离的最小值
diam = d1[center] + up[center] + 1; //直径
}
int rem; //删除重心后剩余连通块体积的最小值
int cog; //重心
vector<bool> vis;
void getCog() {
vis.resize(n);
rem = INT_MAX;
cog = 1;
dfsCog(1);
}
int dfsCog(int u) {
vis[u] = true;
int s = 1, res = 0;
for (auto [w, v]: e[u]) {
if (vis[v]) continue;
int t = dfsCog(v);
res = max(res, t);
s += t;
}
res = max(res, n - s);
if (res < rem) {
rem = res;
cog = u;
}
return s;
}
};
最近公共祖先
struct LCA {
vector<vector<int>> e;
vector<int> top, dep, parent, siz, son;
LCA(int n) {
e.resize(n + 1);
top.resize(n + 1);
dep.resize(n + 1);
parent.resize(n + 1);
siz.resize(n + 1);
son.resize(n + 1);
}
void add(int u, int v) {
e[u].push_back(v);
e[v].push_back(u);
}
void dfs1(int u) {
siz[u] = 1;
dep[u] = dep[parent[u]] + 1;
for (auto v: e[u]) {
if (v == parent[u]) continue;
parent[v] = u;
dfs1(v);
siz[u] += siz[v];
if (siz[v] > siz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int up) {
top[u] = up;
if (son[u]) dfs2(son[u], up);
for (auto v: e[u]) {
if (v == parent[u] || v == son[u]) continue;
dfs2(v, v);
}
}
int lca(int u, int v) {
while (top[u] != top[v]) {
if (dep[top[u]] > dep[top[v]]) {
u = parent[top[u]];
} else {
v = parent[top[v]];
}
}
return dep[u] < dep[v] ? u : v;
}
};
线段树
template<class Info>
struct SegmentTree {
int n;
vector<Info> info;
SegmentTree(vector<Info> init) {
n = init.size();
info.assign(4 * n, Info());
function<void(int, int, int)> build = [&](int p, int l, int r) {
if (l == r) {
info[p] = init[l - 1];
return;
}
int m = (l + r) / 2;
build(2 * p, l, m);
build(2 * p + 1, m + 1, r);
pull(p);
};
build(1, 1, n);
}
void pull(int p) {
info[p] = info[2 * p] + info[2 * p + 1];
}
void rangeApply(int p, int l, int r, int x, int y) {
if () {
return;
}
if (l == r) {
return;
}
int m = (l + r) / 2;
if (x <= m) {
rangeApply(2 * p, l, m, x, y);
}
if (y > m) {
rangeApply(2 * p + 1, m + 1, r, x, y);
}
pull(p);
}
void rangeApply(int l, int r) {
return rangeApply(1, 1, n, l, r);
}
Info rangeQuery(int p, int l, int r, int x, int y) {
if (l > y || r < x) {
return Info();
}
if (l >= x && r <= y) {
return info[p];
}
int m = (l + r) / 2;
return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m + 1, r, x, y);
}
Info rangeQuery(int l, int r) {
return rangeQuery(1, 1, n, l, r);
}
};
struct Info {
LL x = 0, l, r, Max = 0;
};
Info operator+(const Info &a, const Info &b) {
return Info(a.x + b.x, a.l, b.r, max(a.Max, b.Max));
}
懒标记
template<class Info, class Tag>
struct LazySegmentTree {
int n;
vector<Info> info;
vector<Tag> tag;
LazySegmentTree(vector<Info> init) {
n = init.size();
info.assign(4 * n, Info());
tag.assign(4 * n, Tag());
function<void(int, int, int)> build = [&](int p, int l, int r) {
if (l == r) {
info[p] = init[l - 1];
return;
}
int m = (l + r) / 2;
build(2 * p, l, m);
build(2 * p + 1, m + 1, r);
pull(p);
};
build(1, 1, n);
}
void pull(int p) {
info[p] = info[2 * p] + info[2 * p + 1];
}
void apply(int p, const Tag &v) {
info[p].apply(v);
tag[p].apply(v);
}
void push(int p) {
apply(2 * p, tag[p]);
apply(2 * p + 1, tag[p]);
tag[p] = Tag();
}
void rangeApply(int p, int l, int r, int x, int y, const Tag &v) {
if (l > y || r < x) {
return;
}
if (l >= x && r <= y) {
apply(p, v);
return;
}
push(p);
int m = (l + r) / 2;
rangeApply(2 * p, l, m, x, y, v);
rangeApply(2 * p + 1, m + 1, r, x, y, v);
pull(p);
}
void rangeApply(int l, int r, const Tag &v) {
return rangeApply(1, 1, n, l, r, v);
}
Info rangeQuery(int p, int l, int r, int x, int y) {
if (l > y || r < x) {
return Info();
}
if (l >= x && r <= y) {
return info[p];
}
push(p);
int m = (l + r) / 2;
return rangeQuery(2 * p, l, m, x, y) + rangeQuery(2 * p + 1, m + 1, r, x, y);
}
Info rangeQuery(int l, int r) {
return rangeQuery(1, 1, n, l, r);
}
};
struct Tag {
LL x = 0;
void apply(const Tag &t) {
x += t.x;
}
};
struct Info {
LL x = 0, l, r;
void apply(const Tag &t) {
if (t.x) {
x += t.x * (r - l + 1);
}
}
};
Info operator+(const Info &a, const Info &b) {
return Info(a.x + b.x, a.l, b.r);
}
树状数组
template<typename T>
class FenwickTree {
public:
FenwickTree(int _n) : n(_n) {
a.assign(n + 1, T());
}
void add(int x, T v) {
for (int i = x; i <= n; i += i & -i) {
a[i] += v;
}
}
T sum(int x) {
auto ans = T();
for (int i = x; i > 0; i -= i & -i) {
ans += a[i];
}
return ans;
}
T rangeSum(int l, int r) {
return sum(r) - sum(l - 1);
}
private:
int n;
std::vector<T> a;
};
轻重链剖分
struct HLD {
vector<vector<int>> e;
vector<int> top, dep, parent, siz, son, id, a, val;
int idx, mod;
HLD(int n, int P) {
mod = P;
e.resize(n + 1);
top.resize(n + 1);
dep.resize(n + 1);
parent.resize(n + 1);
siz.resize(n + 1);
son.resize(n + 1);
id.resize(n + 1);
idx = 0;
a.resize(n + 1);
val.resize(n + 1);
tr.resize((n << 2) + 1);
}
void add(int u, int v) {
e[u].push_back(v);
e[v].push_back(u);
}
void dfs1(int u) {
siz[u] = 1;
dep[u] = dep[parent[u]] + 1;
for (auto v: e[u]) {
if (v == parent[u]) continue;
parent[v] = u;
dfs1(v);
siz[u] += siz[v];
if (siz[v] > siz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int up) {
id[u] = ++idx;
top[u] = up;
val[idx] = a[u];
if (son[u]) dfs2(son[u], up);
for (auto v: e[u]) {
if (v == parent[u] || v == son[u]) continue;
dfs2(v, v);
}
}
void modifyRange(int u, int v, int k) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
modify(1, id[top[u]], id[u], k);
u = parent[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
modify(1, id[u], id[v], k);
}
LL queryRange(LL u, LL v) {
LL ans = 0;
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
ans = (ans + query(1, id[top[u]], id[u])) % mod;
u = parent[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
return (ans + query(1, id[u], id[v])) % mod;
}
void modifySon(LL u, LL k) {
modify(1, id[u], id[u] + siz[u] - 1, k);
}
LL querySon(LL u) {
return query(1, id[u], id[u] + siz[u] - 1) % mod;
}
};
dsu on tree
struct DsuOnTree {
vector<vector<int>> adj;
vector<int> siz, dep, dfn, idfn, son;
int idx;
DsuOnTree(int n) {
adj.resize(n + 1);
siz.resize(n + 1);
dep.resize(n + 1);
idx = 0;
dfn.resize(n + 1);
idfn.resize(n + 1);
son.resize(n + 1);
}
void addEdge(int u, int v) {
adj[u].push_back(v);
adj[v].push_back(u);
}
void dfs1(int u, int fa) {
siz[u] = 1;
dep[u] = dep[fa] + 1;
dfn[u] = ++idx;
idfn[idx] = u;
for (auto v: adj[u]) {
if (v == fa) continue;
dfs1(v, u);
siz[u] += siz[v];
if (siz[v] > siz[son[u]]) son[u] = v;
}
}
void dfs2(int u, int fa, int opt) {
for (auto v: adj[u]) {
if (v == fa || v == son[u]) continue;
dfs2(v, u, 0);
}
if (son[u]) {
dfs2(son[u], u, 1);
}
for (auto v: adj[u]) {
if (v == fa || v == son[u]) continue;
for (int i = dfn[v]; i <= dfn[v] + siz[v] - 1; i++) {
int x = idfn[i];
}
}
if (!opt) {
for (int i = dfn[u]; i <= dfn[u] + siz[u] - 1; i++) {
int x = idfn[i];
}
}
}
};
并查集
struct DSU {
vector<int> f, siz;
DSU(int n) {
f.resize(n + 1);
iota(f.begin(), f.end(), 0);
siz.assign(n + 1, 1);
}
int find(int x) {
return x == f[x] ? x : (f[x] = find(f[x]));
}
bool same(int x, int y) {
return find(x) == find(y);
}
bool merge(int x, int y) {
x = find(x);
y = find(y);
if (x == y) return false;
if (x < y) swap(x, y);
siz[x] += siz[y];
f[y] = x;
return true;
}
int size(int x) {
return siz[find(x)];
}
};
ST表
struct SparseTable {
int n;
vector<int> a;
vector<vector<int>> mx;
SparseTable(int n) : n(n), a(n + 1), mx(n + 1, vector<int>(__lg(n) + 1)) {}
void init() {
for (int i = 1; i <= n; i++) {
mx[i][0] = a[i];
}
int k = __lg(n);
for (int j = 1; j <= k; j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
mx[i][j] = max(mx[i][j - 1], mx[i + (1 << (j - 1))][j - 1]);
}
}
}
int queryMax(int L, int R) {
int k = __lg(R - L + 1);
return max(mx[L][k], mx[R - (1 << k) + 1][k]);
}
};
可持久化线段树
struct PersistentTree {
static constexpr int N = 2e5;
int idx, root[N + 1];
struct Node {
int l, r;
int info;
} tr[N << 5];
PersistentTree(vector<int> &a) {
int n = a.size();
idx = 0;
function<int(int, int)> build = [&](int l, int r) {
int p = ++idx;
if (l == r) {
tr[p].info = 0;
return p;
}
int mid = (l + r) / 2;
tr[p].l = build(l, mid);
tr[p].r = build(mid + 1, r);
return p;
};
root[0] = build(1, n);
}
int insert(int u, int l, int r, int x) {
int v = ++idx;
tr[v] = tr[u];
tr[v].info++;
if (l == r) return v;
int mid = (l + r) / 2;
if (x <= mid) tr[v].l = insert(tr[u].l, l, mid, x);
else tr[v].r = insert(tr[u].r, mid + 1, r, x);
return v;
}
int query(int u, int v, int l, int r, int k) {
if (l == r) return l;
int mid = (l + r) / 2;
int x = tr[tr[v].l].info - tr[tr[u].l].info;
if (k <= x) return query(tr[u].l, tr[v].l, l, mid, k);
else return query(tr[u].r, tr[v].r, mid + 1, r, k - x);
}
};
KDTree
struct KDT {
constexpr static int K = 2;
double alpha = 0.725;
struct node {
int info[K];
int mn[K], mx[K];
};
vector<node> tr;
vector<int> ls, rs, siz, id, d;
int idx, rt, cur;
int ans;
KDT(int n) {
tr.resize(n + 1);
ls.resize(n + 1);
rs.resize(n + 1);
siz.resize(n + 1);
id.resize(n + 1);
d.resize(n + 1);
rt = 0;
cur = 0;
}
void apply(int p, int son) {
if (son) {
for (int i = 0; i < K; i++) {
tr[p].mn[i] = min(tr[p].mn[i], tr[son].mn[i]);
tr[p].mx[i] = max(tr[p].mx[i], tr[son].mx[i]);
}
siz[p] += siz[son];
}
}
void maintain(int p) {
for (int i = 0; i < K; i++) {
tr[p].mn[i] = tr[p].info[i];
tr[p].mx[i] = tr[p].info[i];
}
siz[p] = 1;
apply(p, ls[p]);
apply(p, rs[p]);
}
int build(int l, int r) {
if (l > r) return 0;
vector<double> avg(K);
for (int i = 0; i < K; i++) {
for (int j = l; j <= r; j++) {
avg[i] += tr[id[j]].info[i];
}
avg[i] /= (r - l + 1);
}
vector<double> var(K);
for (int i = 0; i < K; i++) {
for (int j = l; j <= r; j++) {
var[i] += (tr[id[j]].info[i] - avg[i]) * (tr[id[j]].info[i] - avg[i]);
}
}
int mid = (l + r) / 2;
int x = max_element(var.begin(), var.end()) - var.begin();
nth_element(id.begin() + l, id.begin() + mid, id.begin() + r + 1, [&](int a, int b) {
return tr[a].info[x] < tr[b].info[x];
});
d[id[mid]] = x;
ls[id[mid]] = build(l, mid - 1);
rs[id[mid]] = build(mid + 1, r);
maintain(id[mid]);
return id[mid];
}
void print(int p) {
if (!p) return;
print(ls[p]);
id[++idx] = p;
print(rs[p]);
}
void rebuild(int &p) {
idx = 0;
print(p);
p = build(1, idx);
}
bool bad(int p) {
return alpha * siz[p] <= max(siz[ls[p]], siz[rs[p]]);
}
void insert(int &p, int cur) {
if (!p) {
p = cur;
maintain(p);
return;
}
if (tr[p].info[d[p]] > tr[cur].info[d[p]]) insert(ls[p], cur);
else insert(rs[p], cur);
maintain(p);
if (bad(p)) rebuild(p);
}
void insert(vector<int> &a) {
cur++;
for (int i = 0; i < K; i++) {
tr[cur].info[i] = a[i];
}
insert(rt, cur);
}
bool out(int p, vector<int> &a) {
for (int i = 0; i < K; i++) {
if (a[i] < tr[p].mn[i]) {
return true;
}
}
return false;
}
bool in(int p, vector<int> &a) {
for (int i = 0; i < K; i++) {
if (a[i] < tr[p].info[i]) {
return false;
}
}
return true;
}
bool all(int p, vector<int> &a) {
for (int i = 0; i < K; i++) {
if (a[i] < tr[p].mx[i]) {
return false;
}
}
return true;
}
void query(int p, vector<int> &a) {
if (!p) return;
if (out(p, a)) return;
if (all(p, a)) {
ans += siz[p];
return;
}
if (in(p, a)) ans++;
query(ls[p], a);
query(rs[p], a);
}
int query(vector<int> &a) {
ans = 0;
query(rt, a);
return ans;
}
};
字符串
kmp
std::vector<int> kmp(std::string s) {
int n = s.size();
std::vector<int> next(n + 1);
for (int i = 1, j = 0; i < n; i++) {
while (j > 0 && s[i] != s[j]) {
j = next[j];
}
if (s[i] == s[j]) {
j++;
}
next[i + 1] = j;
}
return next;
}
Z函数
vector<int> zFunction(string s) {
int n = s.size();
vector<int> z(n);
z[0] = n;
for (int i = 1, j = 1; i < n; i++) {
z[i] = max(0, min(j + z[j] - i, z[i - j]));
while (i + z[i] < n && s[z[i]] == s[i + z[i]]) {
z[i]++;
}
if (i + z[i] > j + z[j]) {
j = i;
}
}
return z;
}
manacher
vector<int> manacher(string s) {
string t = "-#";
for (auto c: s) {
t += c;
t += '#';
}
int n = t.size();
t += '+';
vector<int> p(n);
for (int i = 1, mid = 0, r = 0; i < n; i++) {
p[i] = i < r ? min(p[2 * mid - i], r - i) : 1;
while (t[i - p[i]] == t[i + p[i]]) p[i]++;
if (i + p[i] > r) {
r = i + p[i];
mid = i;
}
}
return p;
}
字典树
template<typename T>
class Trie {
public:
Trie(int n) {
idx = 0;
ch.resize(std::__lg(std::numeric_limits<T>::max()) * (n + 1), std::vector<int>(2));
}
int insert(T x) {
int u = 0;
for (int i = std::__lg(std::numeric_limits<T>::max()); ~i; i--) {
int &v = ch[u][x >> i & 1];
if (!v) v = ++idx;
u = v;
}
return u;
}
T query(int x) {
int u = 0;
T res = T();
for (int i = std::__lg(std::numeric_limits<T>::max()); ~i; i--) {
int v = x >> i & 1;
if (ch[u][!v]) {
res += (T)1 << i;
u = ch[u][!v];
} else {
u = ch[u][v];
}
}
return res;
}
private:
int idx;
std::vector<std::vector<int>> ch;
};
AC自动机
Trie+Kmp,多模式串匹配
struct ACAutomaton {
static constexpr int N = 1e6 + 1;
int ch[N][26], fail[N], idx;
int cnt[N];
ACAutomaton() {
idx = 0;
}
void insert(string s) {
int u = 0;
for (auto c: s) {
int &v = ch[u][c - 'a'];
if (!v) v = ++idx;
u = v;
}
cnt[u]++;
}
void build() {
queue<int> q;
for (auto v: ch[0]) {
if (v) {
fail[v] = 0;
q.push(v);
}
}
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i = 0; i < 26; i++) {
int &v = ch[u][i];
if (v) {
fail[v] = ch[fail[u]][i];
q.push(v);
} else {
v = ch[fail[u]][i];
}
}
}
}
int query(string t) {
int u = 0;
int ans = 0;
for (auto c: t) {
u = ch[u][c - 'a'];
for (int v = u; v && ~cnt[v]; v = fail[v]) {
ans += cnt[v];
cnt[v] = -1;
}
}
return ans;
}
};
后缀自动机
有向无环图
struct SuffixAutomaton {
static constexpr int N = 1e6;
struct node {
int len, link, nxt[26];
int siz;
} t[N << 1];
int cntNodes;
SuffixAutomaton() {
cntNodes = 1;
fill(t[0].nxt, t[0].nxt + 26, 1);
t[0].len = -1;
}
int extend(int p, int c) {
if (t[p].nxt[c]) {
int q = t[p].nxt[c];
if (t[q].len == t[p].len + 1) {
return q;
}
int r = ++cntNodes;
t[r].siz = 0;
t[r].len = t[p].len + 1;
t[r].link = t[q].link;
copy(t[q].nxt, t[q].nxt + 26, t[r].nxt);
t[q].link = r;
while (t[p].nxt[c] == q) {
t[p].nxt[c] = r;
p = t[p].link;
}
return r;
}
int cur = ++cntNodes;
t[cur].len = t[p].len + 1;
t[cur].siz = 1;
while (!t[p].nxt[c]) {
t[p].nxt[c] = cur;
p = t[p].link;
}
t[cur].link = extend(p, c);
return cur;
}
};
后缀数组
struct SuffixArray {
int n;
vector<int> sa, rk, lc;
SuffixArray(const string &s) {
n = s.size();
sa.resize(n + 1);
rk.resize(n + 1);
int N = 127;
vector<int> cnt(N + 1);
for (int i = 1; i <= n; i++) {
cnt[rk[i] = s[i - 1]]++;
}
for (int i = 1; i <= N; i++) {
cnt[i] += cnt[i - 1];
}
for (int i = n; i >= 1; i--) {
sa[cnt[rk[i]]--] = i;
}
vector<int> id(n + 1), tmp(n + 1), oldrk(n + 1);
auto cmp = [&](int x, int y, int w) {
return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w];
};
for (int w = 1, p, i;; w *= 2, N = p) {
for (p = 0, i = n; i > n - w; i--) {
id[++p] = i;
}
for (i = 1; i <= n; i++) {
if (sa[i] > w) {
id[++p] = sa[i] - w;
}
}
cnt.assign(N + 1, 0);
for (i = 1; i <= n; i++) {
cnt[tmp[i] = rk[id[i]]]++;
}
for (i = 1; i <= N; i++) {
cnt[i] += cnt[i - 1];
}
for (i = n; i >= 1; i--) {
sa[cnt[tmp[i]]--] = id[i];
}
oldrk = rk;
for (p = 0, i = 1; i <= n; i++) {
rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;
}
if (p == n) break;
}
lc.resize(n + 1);
for (int i = 1, j = 0; i <= n; i++) {
if (rk[i] == 0) continue;
if (j) j--;
while (s[i + j - 1] == s[sa[rk[i] - 1] + j - 1]) j++;
lc[rk[i]] = j;
}
}
};
回文自动机
struct PalindromeAutomaton {
vector<vector<int>> tr;
vector<int> fail, len, cnt;
int idx, last;
PalindromeAutomaton(string s) {
int n = s.size();
tr.resize(n + 2, vector<int>(26));
fail.resize(n + 2);
len.resize(n + 2);
cnt.resize(n + 2);
len[0] = 0, fail[0] = 1;
len[1] = -1, fail[1] = 0;
idx = 1;
last = 0;
int k = 0;
for (int i = 0; i < n; i++) {
s[i] = (s[i] - 97 + k) % 26 + 'a';
int u = last;
while (i - len[u] - 1 < 0 || s[i - len[u] - 1] != s[i]) {
u = fail[u];
}
if (!tr[u][s[i] - 'a']) {
int v = ++idx;
int t = fail[u];
while (i - len[t] - 1 < 0 || s[i - len[t] - 1] != s[i]) {
t = fail[t];
}
fail[v] = tr[t][s[i] - 'a'];
tr[u][s[i] - 'a'] = v;
len[v] = len[u] + 2;
cnt[v] = cnt[fail[v]] + 1;
}
last = tr[u][s[i] - 'a'];
k = cnt[last];
cout << k << " \n"[i == n - 1];
}
}
};
数学
取模整数型
template<class T>
constexpr T power(T a, LL b) {
T res = 1;
for (; b; b /= 2, a *= a) {
if (b % 2) {
res *= a;
}
}
return res;
}
template<int mod>
struct ModZ {
LL x;
constexpr ModZ(LL x = 0) : x(norm(x)) {}
constexpr LL norm(LL x) { return (x % mod + mod) % mod; }
constexpr ModZ inv() { return power(*this, mod - 2); }
constexpr ModZ &operator*=(ModZ rhs) &{
x = norm(x * rhs.x);
return *this;
}
constexpr ModZ &operator+=(ModZ rhs) &{
x = norm(x + rhs.x);
return *this;
}
constexpr ModZ &operator-=(ModZ rhs) &{
x = norm(x - rhs.x);
return *this;
}
constexpr ModZ &operator/=(ModZ rhs) &{ return *this *= rhs.inv(); }
friend constexpr ModZ operator*(ModZ lhs, ModZ rhs) {
ModZ res = lhs;
res *= rhs;
return res;
}
friend constexpr ModZ operator+(ModZ lhs, ModZ rhs) {
ModZ res = lhs;
res += rhs;
return res;
}
friend constexpr ModZ operator-(ModZ lhs, ModZ rhs) {
ModZ res = lhs;
res -= rhs;
return res;
}
friend constexpr ModZ operator/(ModZ lhs, ModZ rhs) {
ModZ res = lhs;
res /= rhs;
return res;
}
friend constexpr istream &operator>>(istream &is, ModZ &a) {
LL v;
is >> v;
a = ModZ(v);
return is;
}
friend constexpr ostream &operator<<(ostream &os, const ModZ &a) { return os << a.x; }
friend constexpr bool operator==(ModZ lhs, ModZ rhs) { return lhs.x == rhs.x; }
friend constexpr bool operator!=(ModZ lhs, ModZ rhs) { return lhs.x != rhs.x; }
};
constexpr int P = 998244353;
using Z = ModZ<P>;
组合数学
struct Combinatorics {
vector<Z> fac, invfac;
Combinatorics(int n) {
fac.resize(n + 1);
invfac.resize(n + 1);
fac[0] = 1;
for (int i = 1; i <= n; i++) {
fac[i] = fac[i - 1] * i;
}
invfac[n] = fac[n].inv();
for (int i = n - 1; i >= 0; i--) {
invfac[i] = invfac[i + 1] * (i + 1);
}
}
Z binom(int n, int m) {
if (n < m || m < 0) return 0LL;
return fac[n] * invfac[m] * invfac[n - m];
}
};
快速傅里叶变换(FFT)
struct Polynomial {
constexpr static double PI = acos(-1);
struct Complex {
double x, y;
Complex(double _x = 0.0, double _y = 0.0) {
x = _x;
y = _y;
}
Complex operator-(const Complex &rhs) const { return Complex(x - rhs.x, y - rhs.y); }
Complex operator+(const Complex &rhs) const { return Complex(x + rhs.x, y + rhs.y); }
Complex operator*(const Complex &rhs) const { return Complex(x * rhs.x - y * rhs.y, x * rhs.y + y * rhs.x); }
};
vector<Complex> c;
Polynomial(vector<int> &a) {
int n = a.size();
c.resize(n);
for (int i = 0; i < n; i++) {
c[i] = Complex(a[i], 0);
}
fft(c, n, 1);
}
void change(vector<Complex> &a, int n) {
for (int i = 1, j = n / 2; i < n - 1; i++) {
if (i < j) swap(a[i], a[j]);
int k = n / 2;
while (j >= k) {
j -= k;
k /= 2;
}
if (j < k) j += k;
}
}
void fft(vector<Complex> &a, int n, int opt) {
change(a, n);
for (int h = 2; h <= n; h *= 2) {
Complex wn(cos(2 * PI / h), sin(opt * 2 * PI / h));
for (int j = 0; j < n; j += h) {
Complex w(1, 0);
for (int k = j; k < j + h / 2; k++) {
Complex u = a[k];
Complex t = w * a[k + h / 2];
a[k] = u + t;
a[k + h / 2] = u - t;
w = w * wn;
}
}
}
if (opt == -1) {
for (int i = 0; i < n; i++) {
a[i].x /= n;
}
}
}
};
快速数论变换(NTT)
struct Polynomial {
vector<Z> z;
vector<int> r;
Polynomial(vector<int> &a) {
int n = a.size();
z.resize(n);
r.resize(n);
for (int i = 0; i < n; i++) {
z[i] = a[i];
r[i] = (i & 1) * (n / 2) + r[i / 2] / 2;
}
ntt(z, n, 1);
}
LL power(LL a, int b) {
LL res = 1;
for (; b; b /= 2, a = a * a % mod) {
if (b % 2) {
res = res * a % mod;
}
}
return res;
}
void ntt(vector<Z> &a, int n, int opt) {
for (int i = 0; i < n; i++) {
if (r[i] < i) {
swap(a[i], a[r[i]]);
}
}
for (int k = 2; k <= n; k *= 2) {
Z gn = power(3, (mod - 1) / k);
for (int i = 0; i < n; i += k) {
Z g = 1;
for (int j = 0; j < k / 2; j++, g *= gn) {
Z t = a[i + j + k / 2] * g;
a[i + j + k / 2] = a[i + j] - t;
a[i + j] = a[i + j] + t;
}
}
}
if (opt == -1) {
reverse(a.begin() + 1, a.end());
Z inv = power(n, mod - 2);
for (int i = 0; i < n; i++) {
a[i] *= inv;
}
}
}
};
拉格朗日插值
struct Lagrange {
int n;
vector<Z> x, y, fac, invfac;
Lagrange(int n) {
this->n = n;
x.resize(n + 3);
y.resize(n + 3);
fac.resize(n + 3);
invfac.resize(n + 3);
init(n);
}
void init(int n) {
iota(x.begin(), x.end(), 0);
for (int i = 1; i <= n + 2; i++) {
Z t;
y[i] = y[i - 1] + t.power(i, n);
}
fac[0] = 1;
for (int i = 1; i <= n + 2; i++) {
fac[i] = fac[i - 1] * i;
}
invfac[n + 2] = fac[n + 2].inv();
for (int i = n + 1; i >= 0; i--) {
invfac[i] = invfac[i + 1] * (i + 1);
}
}
Z solve(LL k) {
if (k <= n + 2) {
return y[k];
}
vector<Z> sub(n + 3);
for (int i = 1; i <= n + 2; i++) {
sub[i] = k - x[i];
}
vector<Z> mul(n + 3);
mul[0] = 1;
for (int i = 1; i <= n + 2; i++) {
mul[i] = mul[i - 1] * sub[i];
}
Z ans = 0;
for (int i = 1; i <= n + 2; i++) {
ans = ans + y[i] * mul[n + 2] * sub[i].inv() * pow(-1, n + 2 - i) * invfac[i - 1] * invfac[n + 2 - i];
}
return ans;
}
};
扩展欧几里得
LL exgcd(LL a, LL b, LL &x, LL &y) {
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
矩阵
struct Matrix {
int n;
vector<vector<Z>> a;
Matrix(int n) {
this->n = n;
a.resize(n, vector<Z>(n));
}
Matrix mul(const Matrix &a, const Matrix &b) {
Matrix res(n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
res.a[i][j] += a.a[i][k] * b.a[k][j];
}
}
}
return res;
}
Matrix power(Matrix a, LL b) {
Matrix res(n);
for (int i = 0; i < n; i++) {
res.a[i][i] = 1;
}
for (; b; b /= 2, a = mul(a, a)) {
if (b % 2) {
res = mul(res, a);
}
}
return res;
}
};
几何
const double eps = 1e-8;
struct point {
double x, y;
point operator+(const point &rhs) const { return point{x + rhs.x, y + rhs.y}; }
point operator-(const point &rhs) const { return point{x - rhs.x, y - rhs.y}; }
point operator*(double rhs) const { return point{x * rhs, y * rhs}; }
point operator/(double rhs) const { return point{x / rhs, y / rhs}; }
};
using vec = point;
double cross(point p1, point p2) { return p1.x * p2.y - p1.y * p2.x; } //叉乘
int sign(double k) {
if (k > eps) return 1;
else if (k < -eps) return -1;
else return 0;
}
int parallel(point p1, point p2, point p3, point p4) { return sign(cross(p1 - p2, p3 - p4)) == 0; } //平行
double dist(point p1, point p2) { return sqrt((p1.x - p2.x) * (p1.x - p2.x) + (p1.y - p2.y) * (p1.y - p2.y)); } //两点间距离
point intersection(point p1, point p2, point p3, point p4) { //两直线交点
double w1 = cross(p1 - p2, p4 - p2);
double w2 = -cross(p1 - p2, p3 - p2);
return (p3 * w1 + p4 * w2) / (w1 + w2);
}
bool onSegment(point p, point s, point t) { return fabs(dist(p, s) + dist(p, t) - dist(s, t)) < eps; } //点是否在线段上
double distPoint2Vector(point p1, vec v1, point p2, vec v2) {
return cross(v2, p2 - p1) / cross(v1, v2);
} //点 p1 到线段 v2 的距离
double intersectionPolygon(point A, point B) { //线段 AB 与多边形的公共长度
vector <pair<double, int>> pos;
vec v = B - A;
for (int i = 0; i < n; i++) {
int sign1 = sign(cross(v, poly[i] - A));
int sign2 = sign(cross(v, poly[(i + 1) % n] - A));
if (sign1 == sign2) continue;
double w = distPoint2Vector(A, v, poly[i], poly[(i + 1) % n] - poly[i]);
pos.push_back({w, sign1 - sign2});
}
sort(pos.begin(), pos.end());
double res = 0;
int sum = 0;
for (int i = 0; i < pos.size(); i++) {
sum += pos[i].second;
if (sum) {
res += pos[i + 1].first - pos[i].first;
}
}
return res * dist(A, B);
};
图论
最大流
template<typename T>
struct Flow {
int n;
vector<pair<int, T>> e;
vector<vector<int>> g;
vector<int> h, cur;
Flow(int n) : n(n), g(n) {}
void addEdge(int u, int v, T c) {
g[u].push_back(e.size());
e.emplace_back(v, c);
g[v].push_back(e.size());
e.emplace_back(u, 0);
}
bool bfs(int s, int t) {
h.assign(n, -1);
h[s] = 0;
queue<int> q;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
for (int i: g[u]) {
auto [v, c] = e[i];
if (c > 0 && h[v] == -1) {
h[v] = h[u] + 1;
if (v == t) return true;
q.push(v);
}
}
}
return false;
}
T dfs(int u, int t, T f) {
if (u == t) return f;
auto r = f;
for (int &i = cur[u]; i < g[u].size(); ++i) {
int j = g[u][i];
auto [v, c] = e[j];
if (c > 0 && h[v] == h[u] + 1) {
auto a = dfs(v, t, min(r, c));
e[j].second -= a;
e[j ^ 1].second += a;
r -= a;
if (r == 0) return f;
}
}
return f - r;
}
T maxFlow(int s, int t) {
T ans = 0;
while (bfs(s, t)) {
cur.assign(n, 0);
ans += dfs(s, t, numeric_limits<T>::max());
}
return ans;
}
};
杂
珂朵莉树
struct ODT {
struct node {
int l, r;
mutable LL v;
node(int l, int r = -1, LL v = 0) : l(l), r(r), v(v) {}
bool operator<(const node &o) const { return l < o.l; }
};
set<node> s;
ODT() {
s.clear();
}
auto split(int pos) {
auto it = s.lower_bound(node(pos));
if (it != s.end() && it->l == pos) return it;
it--;
int l = it->l, r = it->r;
LL v = it->v;
s.erase(it);
s.insert(node(l, pos - 1, v));
return s.insert(node(pos, r, v)).first;
}
void assign(int l, int r, LL x) {
auto itr = split(r + 1), itl = split(l);
s.erase(itl, itr);
s.insert(node(l, r, x));
}
void add(int l, int r, LL x) {
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; it++) {
it->v += x;
}
}
LL kth(int l, int r, int k) {
vector <pair<LL, int>> a;
auto itr = split(r + 1), itl = split(l);
for (auto it = itl; it != itr; it++) {
a.push_back(pair<LL, int>(it->v, it->r - it->l + 1));
}
sort(a.begin(), a.end());
for (auto [val, len]: a) {
k -= len;
if (k <= 0) return val;
}
}
LL power(LL a, int b, int mod) {
a %= mod;
LL res = 1;
for (; b; b /= 2, a = a * a % mod) {
if (b % 2) {
res = res * a % mod;
}
}
return res;
}
LL powersum(int l, int r, int x, int mod) {
auto itr = split(r + 1), itl = split(l);
LL ans = 0;
for (auto it = itl; it != itr; it++) {
ans = (ans + power(it->v, x, mod) * (it->r - it->l + 1) % mod) % mod;
}
return ans;
}
};