这里写自定义目录标题
数据结构
单调队列
// 要求的是每连续的 k 个数中的最大(最小)值,很明显,当一个数进入所要 "寻找" 最大值的范围中时,若这个数比其前面(先进队)的数要大,
//显然,前面的数会比这个数先出队且不再可能是最大值。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int n, k;
cin >> n >> k;
vector<int> v(n + 1);
for (int i = 1; i <= n; i++) cin >> v[i];
deque<int> q1, q2;
for (int i = 1; i <= k; i++) {
while (!q1.empty() && v[i] >= v[q1.back()]) q1.pop_back();
while (!q2.empty() && v[i] <= v[q2.back()]) q2.pop_back();
q1.push_back(i);
q2.push_back(i);
}
vector<pair<int, int>> res;
//res[1].first = v[q1.front()], res[1].second = v[q2.front()];
res.push_back({v[q1.front()], v[q2.front()]});
for (int i = k + 1; i <= n; i++) {
while (!q1.empty() && v[i] >= v[q1.back()]) q1.pop_back();
while (!q2.empty() && v[i] <= v[q2.back()]) q2.pop_back();
q1.push_back(i);
q2.push_back(i);
while (q1.front() <= i - k) q1.pop_front();
while (q2.front() <= i - k) q2.pop_front();
res.push_back({v[q1.front()], v[q2.front()]});
}
for (int i = 0; i <= n - k; i++) cout << res[i].second << " ";
cout << endl;
for (int i = 0; i <= n - k; i++) cout << res[i].first << " ";
cout << endl;
}
单调栈
// 洛谷P5788
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0);
int n;
cin >> n;
vector<int> v(n + 1, 0), sta, res(n + 1);
for (int i = 1; i <= n; i++) cin >> v[i];
for (int i = 1; i <= n; i++) {
while (!sta.empty() && v[sta.back()] < v[i]) res[sta.back()] = i, sta.pop_back();
sta.push_back(i);
}
for (int i = 1; i <= n; i++) cout << res[i] << " ";
}
DSU
#include<bits/stdc++.h>
//
struct DSU {
int fa[maxn];
void init(int n) {
for(int i = 0;i <= n; ++i){
fa[i] = i;
}
}
int find(int x) {
if(x != fa[x]) fa[x] = find(fa[x]);
return fa[x];
}
bool same(int i, int j) {
return find(i) == find(j);
}
void merge(int i, int j) {
fa[find(i)] = find(j);
}
} dsu;
//____________________________________________________________________________________
// 2.带权并查集
struct DSU {
vector<int> fa;
vector<int> w;
DSU(int n) : fa(n + 1), w(n + 1, 0) {
for (int i = 0; i <= n; i++) {
fa[i] = i;
}
}
int find(int x) {
if (x != fa[x]) {
int root = find(fa[x]);
// w[x] = (w[x] + w[fa[x]]) % h;
fa[x] = root;
}
return fa[x];
}
bool same(int i, int j) {
return find(i) == find(j);
}
bool merge(int x, int y, int v) {
int px = find(x), py = find(y);
if (px == py) return false;
// w[px] = (v + w[y] - w[x]) % h;
// if (w[px] < 0) w[px] += h;
fa[px] = py;
return true;
}
};
// _________________________________________________________________
// 3. 构建指定节点i到j的最小生成树
// 但每次merge的边权值v要大于存在边,
// 模拟lca的重构数, weight查找两点间最大值
struct DSU {
vector<int> fa, w;
DSU(int n) {
fa.resize(n, -1);
w.resize(n, 1e9);
}
int find(int x) {
return fa[x] < 0 ? x : find(fa[x]);
}
bool same(int i, int j) {
return find(i) == find(j);
}
bool merge(int x, int y, int v) {
x = find(x), y = find(y);
if (x == y) return false;
if (fa[x] > fa[y]) swap(x, y); // 模拟路径压缩
fa[x] += fa[y], fa[y] = x;
w[y] = v;
return true;
}
int weight(int i, int j) {
int tmp = 0;
while (i != j) {
if (w[i] < w[j]) tmp = w[i], i = fa[i];
else tmp = w[j], j = fa[j];
}
return tmp;
}
};
//___________________________________________________________________________
// 可撤销并查集
struct DSU
{
int fa[maxn];
int sz[maxn];
vector<pair<int&, int>>his_sz;
vector<pair<int&, int>>his_fa;
void init(int n) {
for (int i = 1; i <= n; i++)fa[i] = i, sz[i] = 1;
}
int find(int x) {
while (x != fa[x])x = fa[x];
return x;
}
bool same(int u, int v) {
return find(u) == find(v);
}
void merge(int u, int v) {
int x = find(u);
int y = find(v);
if (x == y) return;
if (sz[x] < sz[y]) std::swap(x, y);
his_sz.push_back({ sz[x], sz[x] });
sz[x] = sz[x] + sz[y];
his_fa.push_back({ fa[y],fa[y] });
fa[y] = x;
}
int histroy() {
return his_fa.size();
}
void roll(int h) {
while (his_fa.size() > h) {
his_fa.back().first = his_fa.back().second;
his_fa.pop_back();
his_sz.back().first = his_sz.back().second;
his_sz.pop_back();
}
}
}dsu;
ST表
#include <iostream>
using namespace std;
const int maxn = 2e5 + 10;
int a[maxn], f[maxn][__lg(maxn) + 1];
void init(int n) {
for (int i = 1; i <= n; i++) f[i][0] = a[i];
for (int j = 1; j <= __lg(n); j++) {
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
f[i][j] = max(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
}
}
int query(int l, int r) {
int s = __lg(r - l + 1);
return max(f[l][s], f[r - (1 << s) + 1][s]);
}
int main() {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
init(n);
int m;
cin >> m;
while (m--) {
int l, r;
cin >> l >> r;
cout << query(l, r) << endl;
}
}
Trie字典树
// ACW 256
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 6e5 + 10, maxm = maxn * 25;
int n, m;
int s[maxn], tr[maxm][2], max_id[maxm], root[maxn], idx;
void insert(int i, int k, int p, int q) {
if (k < 0) {
max_id[q] = i;
return;
}
int val = s[i] >> k & 1;
if (p) tr[q][val ^ 1] = tr[p][val ^ 1];
tr[q][val] = ++idx;
insert(i, k - 1, tr[p][val], tr[q][val]);
max_id[q] = max(max_id[tr[q][0]], max_id[tr[q][1]]);
}
void insert(int i) {
int p = root[i];
int last = 0;
if (i) last = root[i - 1];
for (int j = 23; j >= 0; j--) {
int val = s[i] >> j & 1;
tr[p][val] = ++idx;
if (last) {
tr[p][val ^ 1] = tr[last][val ^ 1];
last = tr[last][val];
}
max_id[idx] = i;
p = tr[p][val];
}
max_id[p] = i;
}
int query(int l, int r, int num) {
int p = root[r];
for (int i = 23; i >= 0; i--) {
int val = num >> i & 1;
if (max_id[tr[p][val ^ 1]] >= l) p = tr[p][val ^ 1];
else p = tr[p][val];
}
return num ^ s[max_id[p]];
}
int main() {
cin >> n >> m;
root[0] = ++idx;
max_id[0] = -1;
insert(0, 23, 0, root[0]);
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
s[i] = s[i - 1] ^ x;
root[i] = ++idx;
insert(i, 23, root[i - 1], root[i]);
}
while (m--) {
char op;
int l, r, x;
cin >> op;
if (op == 'A') {
cin >> x;
n++;
s[n] = s[n - 1] ^ x;
root[n] = ++idx;
insert(n, 23, root[n - 1], root[n]);
}
else {
cin >> l >> r >> x;
cout << query(l - 1, r - 1, s[n] ^ x) << endl;
}
}
return 0;
}
LCA
// 在询问的[l, r]中找p使a[p] ^ ... ^ a[n]最大
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 4e4 + 10, maxm = maxn * 2;
int h[maxn], e[maxm], ne[maxm], idx;
int dep[maxn], fa[maxn][16], q[maxn];
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void bfs(int root) {
memset(dep, 0x3f, sizeof(dep));
dep[0] = 0, dep[root] = 1;
int hh = 0, tt = 0;
q[0] = root;
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (dep[j] > dep[t] + 1) {
dep[j] = dep[t] + 1;
q[++tt] = j;
fa[j][0] = t;
for (int k = 1; k <= 15; k++) {
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
}
int lca(int a, int b) {
if (dep[a] < dep[b]) swap(a, b);
for (int k = 15; k >= 0; k--) {
if (dep[fa[a][k]] >= dep[b]) {
a = fa[a][k];
}
}
if (a == b) return a;
for (int k = 15; k >= 0; k--) {
if (fa[a][k] != fa[b][k]) {
a = fa[a][k];
b = fa[b][k];
}
}
return fa[a][0];
}
int main() {
int n;
cin >> n;
memset(h, -1, sizeof(h));
int root;
for (int i = 1; i <= n; i++) {
int a, b;
cin >> a >> b;
if (b == -1) root = a;
else add(a, b), add(b, a);
}
bfs(root);
int m;
cin >> m;
while (m--) {
int a, b;
cin >> a >> b;
int p = lca(a, b);
if (p == a) cout << 1 << endl;
else if (p == b) cout << 2 << endl;
else cout << 0 << endl;
}
return 0;
}
// 或者
vector<int> g[maxn];
ll dep[maxn], fa[maxn], siz[maxn], son[maxn], top[maxn], res[maxn];
void dfs1(int u, int f) {
dep[u] = dep[f] + 1;
fa[u] = f;
siz[u] = 1;
for (auto j : g[u]) {
if (j == f) continue;
dfs1(j, u);
siz[u] += siz[j];
if (siz[j] > siz[son[u]]) son[u] = j;
}
}
void dfs2(int u, int t) {
top[u] = t;
if (!son[u]) return;
dfs2(son[u], t);
for (auto j : g[u]) {
if (j == fa[u] || j == son[u]) continue;
dfs2(j, j);
}
}
int lca(int x, int y) {
while (top[x] != top[y]) {
if (dep[top[x]] < dep[top[y]]) swap(x, y);
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
树状数组
// 支持单点修改和区间查询, 时间复杂度均为O(logn)
#include <bits/stdc++.h>
#define lowbit(x) ((x) & (-x))
using namespace std;
const int maxn = 5e5 + 5;
int tr[maxn];
inline void update(int i, int x)
{
for (int pos = i; pos < maxn; pos += lowbit(pos))
{
tr[pos] += x;
}
}
inline int query(int n)
{
int res = 0;
for (int pos = n; pos; pos -= lowbit(pos))
{
res += tr[pos];
}
return res;
}
inline int query(int a, int b)
{
return query(b) - query(a - 1);
}
线段树
//它主要用于维护区间信息(要求满足结合律)。
//与树状数组相比,它可以实现 O(log n) 的区间修改,还可以同时支持多种操作
//线段树是一棵平衡二叉树。母结点代表整个区间的和,越往下区间越小。注意,线段树的每个节点都对应一条线段(区间)
#include <iostream>
using ll = long long;
using namespace std;
const int maxn = 2e5 + 5;
struct Node {
int l, r;
ll sum;
//int maxv;
ll lazy;
}tr[maxn * 4];
ll a[maxn];
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
//tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
}
void update(int u, ll val) {
tr[u].sum += (tr[u].r - tr[u].l + 1) * val;
// tr[u].maxv += val;
tr[u].lazy += val;
}
void pushdown(int u) {
if (tr[u].lazy) {
update(u << 1, tr[u].lazy), update(u << 1 | 1, tr[u].lazy);
tr[u].lazy = 0;
}
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r, tr[u].lazy = 0;
if (l == r) {
tr[u].sum = a[l];
// tr[u].maxv = a[l];
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, ll val) {
if (tr[u].l >= l && tr[u].r <= r) {
update(u, val);
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, val);
if (r > mid) modify(u << 1 | 1, l, r, val);
pushup(u);
}
ll querysum(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) {
return tr[u].sum;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
// ll res = 0;
// if (l <= mid) res += querysum(u << 1, l, r);
// if (r > mid) res += querysum(u << 1 | 1, l, r);
// return res;
if (r <= mid) return querysum(u << 1, l, r);
else if (l > mid) return querysum(u << 1 | 1, l, r);
else {
auto left = querysum(u << 1, l, r);
auto right = querysum(u << 1 | 1, l, r);
return left + right;
}
}
/*
int querymax(int u, int l, int r)
{
if (tr[u].l >= l && tr[u].r <= r) return tr[u].maxv;
int mid = tr[u].l + tr[u].r >> 1;
int v = 0;
if (l <= mid) v = querymax(u << 1, l, r);
if (r > mid) v = max(v, querymax(u << 1 | 1, l, r));
return v;
}
*/
// 结构体
struct Segment_Tree {
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].sum = a[l];
// tr[u].maxv = a[l];
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
//tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
}
void update(int u, ll val) {
tr[u].sum += (tr[u].r - tr[u].l + 1) * val;
// tr[u].maxv += val;
tr[u].lazy += val;
}
void pushdown(int u) {
if (tr[u].lazy) {
update(u << 1, tr[u].lazy), update(u << 1 | 1, tr[u].lazy);
tr[u].lazy = 0;
}
}
void modify(int u, int l, int r, ll val) {
if (tr[u].l >= l && tr[u].r <= r) {
update(u, val);
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, val);
if (r > mid) modify(u << 1 | 1, l, r, val);
pushup(u);
}
ll querysum(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) {
return tr[u].sum;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
// ll res = 0;
// if (l <= mid) res += querysum(u << 1, l, r);
// if (r > mid) res += querysum(u << 1 | 1, l, r);
// return res;
if (r <= mid) return query(u << 1, l, r);
else if (l > mid) return query(u << 1 | 1, l, r);
else {
auto left = query(u << 1, l, r);
auto right = query(u << 1 | 1, l, r);
return left + right;
}
}
struct Node {
int l, r;
ll sum;
//int maxv;
ll lazy;
}tr[maxn * 4];
}t1, t2; // 记得要build
// 最大字段和
struct Node {
int l, r;
ll sum;
ll rval;
ll lval;
ll fval;
}tr[maxn * 4];
void pushup(Node &u, Node &l, Node &r) {
u.sum = l.sum + r.sum;
u.lval = max(l.lval, l.sum + r.lval);
u.rval = max(r.rval, r.sum + l.rval);
u.fval = max(l.rval + r.lval, max(l.fval, r.fval));
}
void pushup(int u) {
pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].sum = tr[u].lval = tr[u].rval = tr[u].fval = a[l];
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int x, int v) {
if (tr[u].l == x && tr[u].r == x) {
tr[u] = {x, x, v, v, v, v};
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (x <= mid) modify(u << 1, x, v);
else modify(u << 1 | 1, x, v);
pushup(u);
}
Node query(int u, int l, int r) {
if (l <= tr[u].l && r >= tr[u].r) return tr[u];
int mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) return query(u << 1, l, r);
else if (l > mid) return query(u << 1 | 1, l, r);
else {
auto left = query(u << 1, l, r);
auto right = query(u << 1 | 1, l, r);
Node res;
pushup(res, left, right);
return res;
}
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
build(1, 1, n);
while (m--) {
int op;
cin >> op;
if (op == 1) {
int x, y;
ll val;
cin >> x >> y >> val;
modify(1, x, y, val);
}
else {
int x, y;
cin >> x >> y;
cout << querysum(1, x, y) << endl;
}
}
}
主席树
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n, m;
const int maxn = 1e5 + 10;
int a[maxn], root[maxn], idx;
vector<int> nums;
int find(int x) {
return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
}
struct Node {
int l, r;
int cnt;
}tr[maxn * 4 + maxn * 17];
int build(int l, int r) {
int p = idx++;
if (l == r) {
return p;
}
int mid = l + r >> 1;
tr[p].l = build(l, mid);
tr[p].r = build(mid + 1, r);
return p;
}
int insert(int p, int l, int r, int x) {
int q = idx++;
tr[q] = tr[p];
if (l == r) {
tr[q].cnt++;
return q;
}
int mid = l + r >> 1;
if (x <= mid) tr[q].l = insert(tr[p].l, l, mid, x);
else tr[q].r = insert(tr[p].r, mid + 1, r, x);
tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;
return q;
}
int query(int q, int p, int l, int r, int k) {
if (l == r) return l;
int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
int mid = l + r >> 1;
if (k <= cnt) return query(tr[q].l, tr[p].l, l, mid, k);
else return query(tr[q].r, tr[p].r, mid + 1, r, k - cnt);
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
nums.push_back(a[i]);
}
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
root[0] = build(0, nums.size() - 1);
for (int i = 1; i <= n; i++) {
root[i] = insert(root[i - 1], 0, nums.size() - 1, find(a[i]));
}
while (m--) {
int l, r, k;
cin >> l >> r >> k;
cout << nums[query(root[r], root[l - 1], 0, nums.size() - 1, k)];
}
return 0;
}
分块
#include <iostream>
#include <cstring>
#include <cmath>
using namespace std;
using ll = long long;
const int maxn = 1e5 + 10, maxm = 350;
int n, m, len;
ll add[maxm], sum[maxm];
int w[maxn];
int get(int i) {
return i / len;
}
void change(int l, int r, int d) {
if (get(l) == get(r)) {
for (int i = l; i <= r; i++) {
w[i] += d;
sum[get(i)] += d;
}
}
else {
int i = l, j = r;
while (get(i) == get(l)) w[i] += d, sum[get(i++)] += d;
while (get(j) == get(r)) w[j] += d, sum[get(j--)] += d;
for (int k = get(i); k <= get(j); k++) sum[k] += len * d, add[k] += d;
}
}
ll query(int l, int r) {
ll res = 0;
if (get(l) == get(r)) {
for (int i = l; i <= r; i++) res += w[i] + add[get(i)];
}
else {
int i = l, j = r;
while (get(i) == get(l)) res += w[i] + add[get(i++)];
while (get(j) == get(r)) res += w[j] + add[get(j--)];
for (int k = get(i); k <= get(j); k++) res += sum[k];
}
return res;
}
int main() {
cin >> n >> m;
len = sqrt(n);
for (int i = 1; i <= n; i++) {
cin >> w[i];
sum[get(i)] += w[i];
}
while (m--) {
char op;
int l, r;
cin >> op >> l >> r;
if (op == 'C') {
int d;
cin >> d;
change(l, r, d);
}
else cout << query(l, r) << endl;
}
return 0;
}
莫队
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
const int maxn = 5e4 + 10, maxm = 2e5 + 10, S = 1e6 + 10;
int n, m, len;
int w[maxn], res[maxm];
struct Query {
int id, l, r;
}q[maxm];
int get(int x) {
return x / len;
}
int cnt[S];
void add(int x, int &res) {
if (!cnt[x]) res++;
cnt[x]++;
}
void del(int x, int &res) {
cnt[x]--;
if (!cnt[x]) res--;
}
bool cmp(const Query& a, const Query& b) {
int i = get(a.l), j = get(b.l);
if (i != j) return i < j;
return a.r < b.r;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> w[i];
cin >> m;
len = max(1, (int)sqrt((double)n * n / m));
for (int i = 0; i < m; i++) {
int l, r;
cin >> l >> r;
q[i] = {i, l, r};
}
sort(q, q + m, cmp);
for (int k = 0, i = 0, j = 1, ans = 0; k < m; k++) {
int id = q[k].id, l = q[k].l, r = q[k].r;
while (i < r) add(w[++i], ans);
while (i > r) del(w[i--], ans);
while (j < l) del(w[j++], ans);
while (j > l) add(w[--j], ans);
res[id] = ans;
}
for (int i = 0; i < m; i++) cout << res[i] << endl;
return 0;
}
带修莫队
// CF 1476 G
#include <bits /stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;
const int maxn = 1e5 + 10;
const int maxm = 1e6 + 10;
const ll INF = 1e18;
const int mod = 1e9 + 7;
const double eps = 1e-8;
int n, m, k, q;
int a[maxn], ta[maxn], ans[maxn], cnt[maxn], T[maxn];
struct Query {
int l, r, t, k, id;
}b[maxn];
struct XG {
int pos, x, y;
}d[maxn];
int L[maxn], R[maxn];
void link_del(int u) {
int x = L[u], y = R[u];
R[x] = y, L[y] = x;
}
void link_add(int x, int u) {
int y = R[x];
R[x] = u, L[u] = x;
R[u] = y, L[y] = u;
}
void add(int x) {
if (T[cnt[x] + 1] == 0) link_add(cnt[x], cnt[x] + 1);
if (T[cnt[x]] == 1) link_del(cnt[x]);
T[cnt[x]]--;
cnt[x]++;
T[cnt[x]]++;
}
void del(int x) {
if (T[cnt[x] - 1] == 0) link_add(L[cnt[x]], cnt[x] - 1);
if (T[cnt[x]] == 1) link_del(cnt[x]);
T[cnt[x]]--;
cnt[x]--;
T[cnt[x]]++;
}
void change(int pos, int x, int l, int r) {
if (l <= pos && pos <= r) {
del(a[pos]);
a[pos] = x;
add(a[pos]);
}
else {
a[pos] = x;
}
}
void ToTheMaximum() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
ta[i] = a[i];
}
int tot1 = 0, tot2 = 0;
for (int i = 1; i <= m; i++) {
int op, x, y, k;
cin >> op >> x >> y;
if (op == 1) {
cin >> k;
++tot1;
b[tot1] = {x, y, tot2, k, tot1};
}
else {
++tot2;
d[tot2] = {x, ta[x], y};
ta[x] = y;
}
}
int block = pow(n, 2.0 / 3);
if (block == 0) block = 1;
sort(b + 1, b + tot1 + 1, [&](Query a, Query b) {
int k1 = (a.l - 1) / block;
int k2 = (b.l - 1) / block;
int k3 = (a.r - 1) / block;
int k4 = (b.r - 1) / block;
if (k1 != k2) return k1 < k2;
if (k3 != k4) return k3 < k4;
return a.t < b.t;
});
R[0] = n + 1, L[n + 1] = 0;
T[0] = 1e9;
for (int i = 1, l = 1, r = 0, t = 0; i <= tot1; i++) {
while (l > b[i].l) add(a[--l]);
while (r < b[i].r) add(a[++r]);
while (l < b[i].l) del(a[l++]);
while (r > b[i].r) del(a[r--]);
while (t < b[i].t) t++, change(d[t].pos, d[t].y, l, r);
while (t > b[i].t) change(d[t].pos, d[t].x, l, r), t--;
int &res = ans[b[i].id];
res = 1e9;
int k = b[i].k, p = R[0];
for (int u = R[0]; u != n + 1; u = R[u]) {
while (k > 0 && p != n + 1) {
k -= T[p];
p = R[p];
}
if (k > 0) break;
res = min(res, L[p] - u);
k += T[u];
}
if (res == 1e9) res = -1;
}
for (int i = 1; i <= tot1; i++) cout << ans[i] << endl;
cout << endl;
}
int main() {
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int w_ = 1;
//cin >> w_;
while (w_--) {
ToTheMaximum();
}
return 0;
}
Treap
tree + heap
解决问题: 求排名问题
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10, INF = 1e9;
int n, root, idx;
struct Node {
int l, r;
int key, val;
int cnt, size;
}tr[maxn];
void pushup(int u) {
tr[u].size = tr[u].cnt + tr[tr[u].l].size + tr[tr[u].r].size;
}
int getNode(int key) {
tr[++idx].key = key;
tr[idx].val = rand();
tr[idx].cnt = tr[idx].size = 1;
return idx;
}
void zig(int &p) { // 右旋
int q = tr[p].l;
tr[p].l = tr[q].r, tr[q].r = p, p = q;
pushup(tr[p].r), pushup(p);
}
void zag(int &p) { // 左旋
int q = tr[p].r;
tr[p].r = tr[q].l, tr[q].l = p, p = q;
pushup(tr[p].l), pushup(p);
}
void build() {
getNode(-INF), getNode(INF);
root = 1, tr[root].r = 2;
pushup(root);
if (tr[1].val < tr[2].val) zag(root);
}
void insert(int &p, int key) {
if (!p) p = getNode(key);
else if (tr[p].key == key) tr[p].cnt++;
else if (tr[p].key > key) {
insert(tr[p].l, key);
if (tr[tr[p].l].val > tr[p].val) zig(p);
}
else {
insert(tr[p].r, key);
if (tr[tr[p].r].val > tr[p].val) zag(p);
}
pushup(p);
}
void remove(int &p, int key) {
if (!p) return;
else if (tr[p].key == key) {
if (tr[p].cnt > 1) tr[p].cnt--;
else if (tr[p].l || tr[p].r) {
// 右子树空或左子树val > 右子树val 右旋
if (!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val) {
zig(p);
remove(tr[p].r, key);
}
else {
zag(p);
remove(tr[p].l, key);
}
}
else p = 0;
}
else if (tr[p].key > key) remove(tr[p].l, key);
else remove(tr[p].r, key);
pushup(p);
}
int getRank(int p, int key) {
if (!p) return 0;
if (tr[p].key == key) return tr[tr[p].l].size + 1;
if (tr[p].key > key) return getRank(tr[p].l, key);
return tr[tr[p].l].size + tr[p].cnt + getRank(tr[p].r, key);
}
int getKey(int p, int rank) {
if (!p) return INF;
if (tr[tr[p].l].size >= rank) return getKey(tr[p].l, rank);
if (tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key;
return getKey(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);
}
int getPrev(int p, int key) {
if (!p) return -INF;
if (tr[p].key >= key) return getPrev(tr[p].l, key);
return max(tr[p].key, getPrev(tr[p].r, key));
}
int getNext(int p, int key) {
if (!p) return INF;
if (tr[p].key <= key) return getNext(tr[p].r, key);
return min(tr[p].key, getNext(tr[p].l, key));
}
int main() {
build();
cin >> n;
while (n--) {
int op, x;
cin >> op >> x;
if (op == 1) insert(root, x);
else if (op == 2) remove(root, x);
else if (op == 3) cout << getRank(root, x) - 1 << endl;
else if (op == 4) cout << getKey(root, x + 1) << endl;
else if (op == 5) cout << getPrev(root, x) << endl;
else cout << getNext(root, x) << endl;
}
return 0;
}
Splay
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n, m, root, idx;
struct Node {
int s[2], p, v;
int size, flag;
void init(int v_, int p_) {
v = v_;
p = p_;
size = 1;
}
}tr[maxn];
void pushup(int x) {
tr[x].size = tr[tr[x].s[0]].size + tr[tr[x].s[1]].size + 1;
}
void pushdown(int x) {
if (tr[x].flag) {
swap(tr[x].s[0], tr[x].s[1]);
tr[tr[x].s[0]].flag ^= 1;
tr[tr[x].s[1]].flag ^= 1;
tr[x].flag = 0;
}
}
void rotate(int x) {
int y = tr[x].p, z = tr[y].p;
int k = tr[y].s[1] == x; // 0 左儿子 1 右儿子
tr[z].s[tr[z].s[1] == y] = x, tr[x].p = z;
tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
tr[x].s[k ^ 1] = y, tr[y].p = x;
pushup(y), pushup(x);
}
void splay(int x, int k) {
while (tr[x].p != k) {
int y = tr[x].p, z = tr[y].p;
if (z != k) {
if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
else rotate(y);
}
rotate(x);
}
if (!k) root = x;
}
void insert(int v) {
int u = root;
int p = 0;
while (u) p = u, u = tr[u].s[v > tr[u].v];
u = ++idx;
if (p) tr[p].s[v > tr[p].v] = u;
tr[u].init(v, p);
splay(u, 0);
}
int get_k(int k) {
int u = root;
while (true) {
pushdown(u);
if (tr[tr[u].s[0]].size >= k) u = tr[u].s[0];
else if (tr[tr[u].s[0]].size + 1 == k) return u;
else k -= tr[tr[u].s[0]].size + 1, u = tr[u].s[1];
}
return -1;
}
void output(int u) {
pushdown(u);
if (tr[u].s[0]) output(tr[u].s[0]);
if (tr[u].v >= 1 && tr[u].v <= n) cout << tr[u].v << " ";
if (tr[u].s[1]) output(tr[u].s[1]);
}
int main() {
cin >> n >> m;
for (int i = 0; i <= n + 1; i++) insert(i);
while (m--) {
int l, r;
cin >> l >> r;
l = get_k(l), r = get_k(r + 2);
splay(l, 0), splay(r, l);
tr[tr[r].s[0]].flag ^= 1;
}
output(root);
return 0;
}
树链剖分
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
using ll = long long;
const int maxn = 1e5 + 10, maxm = maxn * 2;
int n, m;
int w[maxn], h[maxn], e[maxm], ne[maxm], idx;
int id[maxn], nw[maxn], cnt;
int dep[maxn], siz[maxn], top[maxn], fa[maxn], son[maxn];
struct Tree {
int l, r;
ll add, sum;
}tr[maxn * 4];
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void dfs1(int u, int f, int d) {
dep[u] = d, fa[u] = f, siz[u] = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == f) continue;
dfs1(j, u, d + 1);
siz[u] += siz[j];
if (siz[son[u]] < siz[j]) son[u] = j;
}
}
void dfs2(int u, int t) {
id[u] = ++cnt, nw[cnt] = w[u], top[u] = t;
if (!son[u]) return;
dfs2(son[u], t);
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa[u] || j == son[u]) continue;
dfs2(j, j);
}
}
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u) {
auto &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
if (root.add) {
left.add += root.add, left.sum += root.add * (left.r - left.l + 1);
right.add += root.add, right.sum += root.add * (right.r - right.l + 1);
root.add = 0;
}
}
void build(int u, int l, int r) {
tr[u] = {l, r, 0, nw[r]};
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void update(int u, int l, int r, int k) {
if (l <= tr[u].l && r >= tr[u].r) {
tr[u].add += k;
tr[u].sum += k * (tr[u].r - tr[u].l + 1);
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) update(u << 1, l, r, k);
if (r > mid) update(u << 1 | 1, l, r, k);
pushup(u);
}
ll query(int u, int l, int r) {
if (l <= tr[u].l && r >= tr[u].r) return tr[u].sum;
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
ll res = 0;
if (l <= mid) res += query(u << 1, l, r);
if (r > mid) res += query(u << 1 | 1, l, r);
return res;
}
void update_path(int u, int v, int k) {
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
update(1, id[top[u]], id[u], k);
u = fa[top[u]];
}
if (dep[u] < dep[v]) swap(u, v);
update(1, id[v], id[u], k);
}
ll query_path(int u, int v) {
ll res = 0;
while (top[u] != top[v]) {
if (dep[top[u]] < dep[top[v]]) swap(u, v);
res += query(1, id[top[u]], id[u]);
u = fa[top[u]];
}
if (dep[u] < dep[v]) swap(u, v);
res += query(1, id[v], id[u]);
return res;
}
void update_tree(int u, int k) {
update(1, id[u], id[u] + siz[u] - 1, k);
}
ll query_tree(int u) {
return query(1, id[u], id[u] + siz[u] - 1);
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> w[i];
memset(h, -1, sizeof(h));
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
dfs1(1, -1, 1);
dfs2(1, 1);
build(1, 1, n);
cin >> m;
while (m--) {
int t, u, v, k;
cin >> t >> u;
if (t == 1) {
cin >> v >> k;
update_path(u, v, k);
}
else if (t == 2) {
cin >> k;
update_tree(u, k);
}
else if (t == 3) {
cin >> v;
cout << query_path(u, v) << endl;
}
else cout << query_tree(u) << endl;
}
return 0;
}
Another树剖
using Edge = int;
struct HLD{
int n;
vector<int> sz, top, dep, fa, in, out, seq;
vector<vector<Edge> > g;
int ts;
HLD(const vector<vector<Edge> > &g, int root = 1) : n(int(g.size()) - 1), g(g) {
ts = 0;
sz.resize(n + 1);
top.resize(n + 1);
dep.resize(n + 1);
fa.resize(n + 1);
in.resize(n + 1);
out.resize(n + 1);
seq.resize(n + 1);
dep[root] = 1;
top[root] = root;
dfs_sz(root);
dfs_hld(root);
}
void dfs_sz(int u){
if (fa[u]){
for(auto it = g[u].begin(); it != g[u].end(); it++){
if (*it == fa[u]){
g[u].erase(it);
break;
}
}
}
sz[u] = 1;
for(auto &j : g[u]){
fa[j] = u;
dep[j] = dep[u] + 1;
dfs_sz(j);
sz[u] += sz[j];
if (sz[j] > sz[g[u][0]])
swap(j, g[u][0]);
}
}
void dfs_hld(int u){
in[u] = ++ts;
seq[in[u]] = u;
for (auto j : g[u]){
top[j] = (j == g[u][0] ? top[u] : j);
dfs_hld(j);
}
out[u] = ts;
}
int lca(int u, int v){
while (top[u] != top[v]){
if (dep[top[u]] > dep[top[v]]){
u = fa[top[u]];
}
else{
v = fa[top[v]];
}
}
return dep[u] < dep[v] ? u : v;
}
int dist(int u, int v){
return dep[u] + dep[v] - 2 * dep[lca(u, v)];
}
bool in_subtree(int u, int v){
return in[v] <= in[u] && in[u] <= out[v];
}
int jump(int u, int k) {
if (dep[u] < k){
return -1;
}
int d = dep[u] - k;
while (dep[top[u]] > d){
u = fa[top[u]];
}
return seq[in[u] - dep[u] + d];
}
int rooted_lca(int a, int b, int c){
return lca(a, b) ^ lca(b, c) ^ lca(c, a);
}
template<typename Q>
void modify_path(int u, int v, const Q &q, bool edge = false){
while(top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) swap(u, v);
q(in[top[u]], in[u]);
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
q(in[u] + edge, in[v]);
}
template<typename Q>
void modify_subtree(int u, const Q &q){
q(in[u], out[u]);
}
template<typename T, typename Q>
T query_path(int u, int v, const Q &q, bool edge = false){
T ret = T();
while(top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) swap(u, v);
ret = q(in[top[u]], in[u]) + ret;
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u, v);
return q(in[u] + edge, in[v]) + ret;
}
template<typename T, typename Q>
T query_subtree(int u, const Q &q){
return q(in[u], out[u]);
}
template<typename T, typename Q, typename F>
T query_path_noncommutative(int u, int v, const Q &q, const F &f, bool edge = false){
T left = T(), right = T();
while(top[u] != top[v]){
if (dep[top[u]] < dep[top[v]]) swap(u, v), swap(left, right);
left = q(in[top[u]], in[u]) + left;
u = fa[top[u]];
}
if (dep[u] > dep[v]) swap(u, v), swap(left, right);
return f(left, q(in[u] + edge, in[v]) + right);
}
};
struct Info{
int lmn = 0, rmn = 0, lmx = 0, rmx = 0, mn = 0, mx = 0, sum = 0;
};
Info operator+(const Info &a, const Info &b){
Info ret;
ret.sum = a.sum + b.sum;
ret.lmn = min(a.lmn, a.sum + b.lmn);
ret.lmx = max(a.lmx, a.sum + b.lmx);
ret.rmn = min(b.rmn, b.sum + a.rmn);
ret.rmx = max(b.rmx, b.sum + a.rmx);
ret.mn = min({a.mn, a.rmn + b.lmn, b.mn});
ret.mx = max({a.mx, a.rmx + b.lmx, b.mx});
return ret;
}
template<class Info>
struct SegmentTree{
int n;
vector<Info> info;
SegmentTree() {}
SegmentTree(int n, Info _init = Info()){
init(vector<Info>(n, _init));
}
SegmentTree(const vector<Info> &_init){
init(_init);
}
void init(const vector<Info> &_init){
n = (int)_init.size();
info.assign((n << 2) + 1, 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 modify(int p, int l, int r, int x, const Info &v){
if (l == r){
info[p] = v;
return;
}
int m = (l + r) / 2;
if (x <= m){
modify(2 * p, l, m, x, v);
}
else{
modify(2 * p + 1, m + 1, r, x, v);
}
pull(p);
}
void modify(int p, const Info &v){
modify(1, 1, n, p, v);
}
Info query(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 query(2 * p, l, m, x, y) + query(2 * p + 1, m + 1, r, x, y);
}
Info query(int l, int r){
return query(1, 1, n, l, r);
}
};
树套树
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
const int maxn = 5e4 + 10, maxm = maxn * 4, INF = 1e9;
int n, m, w[maxn];
struct Node {
int l, r;
multiset<int> s;
}tr[maxm];
void build(int u, int l, int r) {
tr[u] = {l, r};
tr[u].s.insert(-INF);
for (int i = l; i <= r; i++) tr[u].s.insert(w[i]);
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
void update(int u, int pos, int v) {
tr[u].s.erase(tr[u].s.find(w[pos]));
tr[u].s.insert(v);
if (tr[u].l == tr[u].r) return;
int mid = tr[u].l + tr[u].r >> 1;
if (pos <= mid) update(u << 1, pos, v);
else update(u << 1 | 1, pos, v);
}
int query(int u, int l, int r, int x) {
if (tr[u].l >= l && tr[u].r <= r) {
auto it = tr[u].s.lower_bound(x);
return *prev(it);
}
int mid = tr[u].l + tr[u].r >> 1;
int res = -INF;
if (l <= mid) res = max(res, query(u << 1, l, r, x));
if (r > mid) res = max(res, query(u << 1 | 1, l, r, x));
return res;
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> w[i];
build(1, 1, n);
while (m--) {
int op, a, b, x;
cin >> op;
if (op == 1) {
cin >> a >> x;
update(1, a, x);
w[a] = x;
}
else {
cin >> a >> b >> x;
cout << query(1, a, b, x) << endl;
}
}
return 0;
}
外部线段树离散化开maxn * 4个点
内部线段树动态开maxn * log(n) * log(n)个点
#include <iostream>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
using ll = long long;
const int maxn = 5e4 + 10, maxm = maxn * 4;
int n, m;
int L[maxm], R[maxm], T[maxm], idx;
struct Query {
int op, a, b, c;
}q[maxn];
vector<int> nums;
int get(int x) {
return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
}
namespace treeIn {
struct inTree {
int l, r;
ll sum, add;
};
inTree tr2[maxn * 17 * 17];
void pushup(int u) {
tr2[u].sum = tr2[tr2[u].l].sum + tr2[tr2[u].r].sum;
}
void pushdown(int u, int l, int r) {
if (!tr2[u].add) return;
if (!tr2[u].l) tr2[u].l = ++idx;
if (!tr2[u].r) tr2[u].r = ++idx;
int mid = l + r >> 1;
inTree &fa = tr2[u], &ls = tr2[tr2[u].l], &rs = tr2[tr2[u].r];
ls.add += fa.add, rs.add += fa.add;
ls.sum += fa.add * (mid - l + 1), rs.sum += fa.add * (r - mid);
fa.add = 0;
}
void modify2(int u, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) {
tr2[u].add++;
tr2[u].sum += r - l + 1;
return;
}
pushdown(u, l, r);
int mid = l + r >> 1;
if (ql <= mid) {
if (!tr2[u].l) tr2[u].l = ++idx;
modify2(tr2[u].l, l, mid, ql, qr);
}
if (qr > mid) {
if (!tr2[u].r) tr2[u].r = ++idx;
modify2(tr2[u].r, mid + 1, r, ql, qr);
}
pushup(u);
}
ll query2(int u, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return tr2[u].sum;
pushdown(u, l, r);
ll res = 0;
int mid = l + r >> 1;
if (ql <= mid) res += query2(tr2[u].l, l, mid, ql, qr);
if (qr > mid) res += query2(tr2[u].r, mid + 1, r, ql, qr);
return res;
}
}
using namespace treeIn;
namespace treeOut {
struct outTree {
int l, r;
int root;
};
outTree tr1[maxn * 4];
void build(int u, int l, int r) {
tr1[u] = {l, r, ++idx};
if (l == r) return;
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
}
void modify1(int u, int l, int r, int x) {
modify2(tr1[u].root, 1, n, l, r);
if (tr1[u].l == tr1[u].r) return;
int mid = tr1[u].l + tr1[u].r >> 1;
if (x <= mid) modify1(u << 1, l, r, x);
else modify1(u << 1 | 1, l, r, x);
}
int query1(int u, int l, int r, int k) {
if (tr1[u].l == tr1[u].r) return tr1[u].l;
ll cnt = query2(tr1[u << 1 | 1].root, 1, n, l, r);
int mid = tr1[u].l + tr1[u].r >> 1;
if (cnt >= k) return query1(u << 1 | 1, l, r, k);
else return query1(u << 1, l, r, k - cnt);
}
}
using namespace treeOut;
int main() {
cin >> n >> m;
for (int i = 0; i < m; i++) {
cin >> q[i].op >> q[i].a >> q[i].b >> q[i].c;
if (q[i].op == 1) nums.push_back(q[i].c);
}
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
build(1, 0, nums.size() - 1);
for (int i = 0; i < m; i++) {
int op = q[i].op, a = q[i].a, b = q[i].b, c = q[i].c;
if (op == 1) modify1(1, a, b, get(c));
else cout << nums[query1(1, a, b, c)] << endl;
}
return 0;
}
Link Cut Tree
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 1e5 + 10;
int n, m;
struct Node {
int s[2], p, v;
int sum, rev;
}tr[maxn];
int stk[maxn];
void pushrev(int x) {
swap(tr[x].s[0], tr[x].s[1]);
tr[x].rev ^= 1;
}
void pushup(int x) {
tr[x].sum = tr[tr[x].s[0]].sum ^ tr[x].v ^ tr[tr[x].s[1]].sum;
}
void pushdown(int x) {
if (tr[x].rev) {
pushrev(tr[x].s[0]), pushrev(tr[x].s[1]);
tr[x].rev = 0;
}
}
bool isroot(int x) {
return tr[tr[x].p].s[0] != x && tr[tr[x].p].s[1] != x;
}
void rotate(int x) {
int y = tr[x].p, z = tr[y].p;
int k = tr[y].s[1] == x;
if (!isroot(y)) tr[z].s[tr[z].s[1] == y] = x;
tr[x].p = z;
tr[y].s[k] = tr[x].s[k ^ 1], tr[tr[x].s[k ^ 1]].p = y;
tr[x].s[k ^ 1] = y, tr[y].p = x;
pushup(y), pushup(x);
}
void splay(int x) {
int top = 0, r = x;
stk[++top] = r;
while (!isroot(r)) stk[++top] = r = tr[r].p;
while (top) pushdown(stk[top--]);
while (!isroot(x)) {
int y = tr[x].p, z = tr[y].p;
if (!isroot(y)) {
if ((tr[y].s[1] == x) ^ (tr[z].s[1] == y)) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x) {
int z = x;
for (int y = 0; x; y = x, x = tr[x].p) {
splay(x);
tr[x].s[1] = y, pushup(x);
}
splay(z);
}
void makeroot(int x) {
access(x);
pushrev(x);
}
int findroot(int x) {
access(x);
while (tr[x].s[0]) pushdown(x), x = tr[x].s[0];
splay(x);
return x;
}
void split(int x, int y) {
makeroot(x);
access(y);
}
void link(int x, int y) {
makeroot(x);
if (findroot(y) != x) tr[x].p = y;
}
void cut(int x, int y) {
makeroot(x);
if (findroot(y) == x && tr[y].p == x && !tr[y].s[0]) {
tr[x].s[1] = tr[y].p = 0;
pushup(x);
}
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> tr[i].v;
while (m--) {
int op, x, y;
cin >> op >> x >> y;
if (op == 0) {
split(x, y);
cout << tr[y].sum << endl;
}
else if (op == 1) link(x, y);
else if (op == 2) cut(x, y);
else {
splay(x);
tr[x].v = y;
pushup(x);
}
}
return 0;
}
点分治
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 10010, maxm = maxn * 2;
int n, m;
int h[maxn], e[maxm], w[maxm], ne[maxm], idx;
bool vis[maxn];
int p[maxn], q[maxn];
void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
int get_size(int u, int fa) {
if (vis[u]) return 0;
int res = 1;
for (int i = h[u]; ~i; i = ne[i]) {
if (e[i] != fa) res += get_size(e[i], u);
}
return res;
}
int get_wc(int u, int fa, int tot, int &wc) {
if (vis[u]) return 0;
int sum = 1, ms = 0;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
int t = get_wc(j, u, tot, wc);
ms = max(ms, t);
sum += t;
}
ms = max(ms, tot - sum);
if (ms <= tot / 2) wc = u;
return sum;
}
void get_dis(int u, int fa, int dis, int &qt) {
if (vis[u]) return;
q[qt++] = dis;
for (int i = h[u]; ~i; i = ne[i]) {
if (e[i] != fa) {
get_dis(e[i], u, dis + w[i], qt);
}
}
}
int get(int a[], int k) {
sort(a, a + k);
int res = 0;
for (int i = k - 1, j = -1; i >= 0; i--) {
while (j + 1 < i && a[j + 1] + a[i] <= m) j++;
j = min(j, i - 1);
res += j + 1;
}
return res;
}
int cal(int u) {
if (vis[u]) return 0;
int res = 0;
get_wc(u, -1, get_size(u, -1), u);
vis[u] = true;
int pt = 0;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i], qt = 0;
get_dis(j, -1, w[i], qt);
res -= get(q, qt);
for (int k = 0; k < qt; k++) {
if (q[k] <= m) res++;
p[pt++] = q[k];
}
}
res += get(p, pt);
for (int i = h[u]; ~i; i = ne[i]) res += cal(e[i]);
return res;
}
int main() {
while (cin >> n >> m && (n || m)) {
memset(vis, 0, sizeof(vis));
memset(h, -1, sizeof(h));
idx = 0;
for (int i = 1; i < n; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
cout << cal(0) << endl;
}
return 0;
}
点分树
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
using ll = long long;
const int maxn = 1.5e5 + 10, maxm = maxn * 2;
int n, m, A;
int h[maxn], e[maxm], ne[maxm], w[maxm], idx;
int age[maxn];
bool vis[maxn];
struct Father {
int u, num; // 重心u 第num个子树
ll dis; // 距离u dis
};
vector<Father> f[maxn];
struct Son {
int age;
ll dis;
bool operator < (const Son& other) const {
return age < other.age;
}
};
vector<Son> son[maxn][3];
void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
int get_size(int u, int fa) {
if (vis[u]) return 0;
int res = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
res += get_size(j, u);
}
return res;
}
int get_wc(int u, int fa, int tot, int &wc) {
if (vis[u]) return 0;
int sum = 1, ms = 0;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
int t = get_wc(j, u, tot, wc);
ms = max(ms, t);
sum += t;
}
ms = max(ms, tot - sum);
if (ms <= tot / 2) wc = u;
return sum;
}
void get_dis(int u, int fa, ll dis, int wc, int k, vector<Son> &p) {
if (vis[u]) return;
f[u].push_back({wc, k, dis});
p.push_back({age[u], dis});
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
get_dis(j, u, dis + w[i], wc, k, p);
}
}
void cal(int u) {
if (vis[u]) return;
get_wc(u, -1, get_size(u, -1), u);
vis[u] = true;
for (int i = h[u], k = 0; ~i; i = ne[i]) {
int j = e[i];
if (vis[j]) continue;
auto &p = son[u][k];
p.push_back({-1, 0}), p.push_back({A + 1, 0});
get_dis(j, -1, w[i], u, k, p);
k++;
sort(p.begin(), p.end());
for (int i = 1; i < p.size(); i++) p[i].dis += p[i - 1].dis;
}
for (int i = h[u]; ~i; i = ne[i]) cal(e[i]);
}
ll query(int u, int l, int r) {
ll res = 0;
for (int i = 0; i < f[u].size(); i++) {
auto &t = f[u][i];
int g = age[t.u];
if (g >= l && g <= r) res += t.dis;
for (int j = 0; j < 3; j++) {
if (j == t.num) continue;
auto &p = son[t.u][j];
if (p.empty()) continue;
int a = lower_bound(p.begin(), p.end(), Son({l, -1})) - p.begin();
int b = lower_bound(p.begin(), p.end(), Son({r + 1, -1})) - p.begin() - 1;
res += t.dis * (b - a + 1) + p[b].dis - p[a - 1].dis;
}
}
for (int i = 0; i < 3; i++) {
auto &p = son[u][i];
if (p.empty()) continue;
int a = lower_bound(p.begin(), p.end(), Son{l, -1}) - p.begin();
int b = lower_bound(p.begin(), p.end(), Son({r + 1, -1})) - p.begin() - 1;
res += p[b].dis - p[a - 1].dis;
}
return res;
}
int main() {
cin >> n >> m >> A;
for (int i = 1; i <= n; i++) cin >> age[i];
memset(h, -1, sizeof(h));
for (int i = 1; i < n; i++) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
add(b, a, c);
}
cal(1);
ll res = 0;
while (m--) {
int u, a, b;
cin >> u >> a >> b;
int l = (a + res) % A, r = (b + res) % A;
if (l > r) swap(l, r);
res = query(u, l, r);
cout << res << endl;
}
return 0;
}
CDQ分治
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 100010, maxm = 2 * maxn;
int n, m;
struct Data {
int a, b, c, s, res;
bool operator < (const Data& t) const {
if (a != t.a) return a < t.a;
if (b != t.b) return b < t.b;
return c < t.c;
}
bool operator == (const Data& t) const {
return a == t.a && b == t.b && c == t.c;
}
}q[maxn], w[maxn];
int tr[maxm], res[maxn];
int lowbit(int x) {
return x & -x;
}
void add(int x, int v) {
for (int i = x; i < maxm; i += lowbit(i)) tr[i] += v;
}
int query(int x) {
int res = 0;
for (int i = x; i; i -= lowbit(i)) res += tr[i];
return res;
}
void merge_sort(int l, int r) {
if (l >= r) return;
int mid = l + r >> 1;
merge_sort(l, mid), merge_sort(mid + 1, r);
int i = l, j = mid + 1, k = 0;
while (i <= mid && j <= r) {
if (q[i].b <= q[j].b) add(q[i].c, q[i].s), w[k++] = q[i++];
else q[j].res += query(q[j].c), w[k++] = q[j++];
}
while (i <= mid) add(q[i].c, q[i].s), w[k++] = q[i++];
while (j <= r) q[j].res += query(q[j].c), w[k++] = q[j++];
for (i = l; i <= mid; i++) add(q[i].c, -q[i].s);
for (i = l, j = 0; j < k; i++, j++) q[i] = w[j];
}
int main() {
cin >> n >> m;
for (int i = 0; i < n; i++) {
int a, b, c;
cin >> a >> b >> c;
q[i] = {a, b, c, 1};
}
sort(q, q + n);
int k = 1;
for (int i = 1; i < n; i++) {
if (q[i] == q[k - 1]) q[k - 1].s++;
else q[k++] = q[i];
}
merge_sort(0, k - 1);
for (int i = 0; i < k; i++) {
res[q[i].res + q[i].s - 1] += q[i].s;
}
for (int i = 0; i < n; i++) cout << res[i] << endl;
return 0;
}
DSU On Tree
// CF 600E
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
using ll = long long;
const int maxn = 1e5 + 10, maxm = maxn * 2;
int n;
vector<int> g[maxn];
int col[maxn], cnt[maxn], siz[maxn], son[maxn];
ll res[maxn], sum;
int mx;
void dfs1(int u, int fa) {
siz[u] = 1;
for (auto j : g[u]) {
if (j == fa) continue;
dfs1(j, u);
siz[u] += siz[j];
if (siz[j] > siz[son[u]]) son[u] = j;
}
}
void update(int u, int fa, int sign, int pson) {
int c = col[u];
cnt[c] += sign;
if (cnt[c] > mx) mx = cnt[c], sum = c;
else if (cnt[c] == mx) sum += c;
for (auto j : g[u]) {
if (j == fa || j == pson) continue;
update(j, u, sign, pson);
}
}
void dfs2(int u, int fa, int op) {
for (auto j : g[u]) {
if (j == fa || j == son[u]) continue;
dfs2(j, u, 0);
}
if (son[u]) dfs2(son[u], u, 1);
update(u, fa, 1, son[u]);
res[u] = sum;
if (!op) update(u, fa, -1, 0), sum = mx = 0;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> col[i];
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs1(1, 0);
dfs2(1, 0, 1);
for (int i = 1; i <= n; i++) cout << res[i] << " ";
return 0;
}
#include <bits/stdc++.h>
using namespace std;
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0)
#define endl '\n'
using ll = long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;
const int maxn = 2e5 + 10;
const int maxm = 2;
const ll INF = 1e18;
const int mod = 1e9 + 7;
int n, m, k, c;
vector<int> g[maxn];
int a[maxn];
set<int> se[maxn];
int res = 0;
void dfs(int u, int fa) {
a[u] ^= a[fa];
map<int, int> mp;
int mx = 0;
for (auto j : g[u]) {
if (j == fa) continue;
dfs(j, u);
if (se[u].size() < se[j].size()) swap(se[u], se[j]);
for (auto x : se[j]) {
if (se[u].count(x)) {
if (mp[x] == 0) mp[x] = 2;
else mp[x]++;
mx = max(mx, mp[x]);
}
else {
se[u].insert(x);
}
}
}
if (u > 1 && g[u].size() == 1) {
res++;
se[u].insert(a[u]);
}
if (mx > 1) {
res -= mx - 1;
se[u].clear();
for (auto [x, y] : mp) {
if (y == mx) se[u].insert(x);
}
}
}
void Jared_McDs() {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1, 0);
res -= se[1].count(0);
cout << res << endl;
}
int main() {
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
IOS;
int w_ = 1;
// cin >> w_;
while (w_--) {
Jared_McDs();
}
return 0;
}
Kruskal
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e4 + 10, maxm = 5e4 + 10, INF = 1e9;
int n, m;
int p[maxn], h[maxn], e[maxm], ne[maxm], v[maxm], idx;
int dep[maxn], f[maxn], fa[maxn][21], w[maxn][21], vis[maxn];
struct Edge1 {
int x, y, v;
bool operator<(const Edge1 &oth) const {
return v > oth.v;
}
}edge1[maxm];
int find(int x) {
if (p[x] != x) {
return p[x] = find(p[x]);
}
return p[x];
}
void add(int a, int b, int c) {
e[idx] = b;
v[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
void kruskal() {
sort(edge1 + 1, edge1 + m + 1);
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = 1; i <= m; i++) {
int a = edge1[i].x, b = edge1[i].y, c = edge1[i].v;
int pa = find(a), pb = find(b);
if (pa != pb) {
p[pa] = pb;
add(a, b, c);
add(b, a, c);
}
}
}
void dfs(int u) {
vis[u] = 1;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (vis[j]) continue;
dep[j] = dep[u] + 1;
fa[j][0] = u;
w[j][0] = v[i];
dfs(j);
}
}
int lca(int x, int y) {
if (find(x) != find(y)) return -1;
int res = INF;
if (dep[x] > dep[y]) swap(x, y);
for (int i = 20; i >= 0; i--) {
if (dep[fa[y][i]] >= dep[x]) {
res = min(res, w[y][i]);
y = fa[y][i];
}
}
if (x == y) return res;
for (int i = 20; i >= 0; i--) {
if (fa[x][i] != fa[y][i]) {
res = min(res, min(w[x][i], w[y][i]));
x = fa[x][i];
y = fa[y][i];
}
}
res = min(res, min(w[x][0], w[y][0]));
return res;
}
int main() {
memset(h, -1, sizeof(h));
cin >> n >> m;
for (int i = 1; i <= m; i++) {
cin >> edge1[i].x >> edge1[i].y >> edge1[i].v;
}
kruskal();
for (int i = 1; i <= n; i++) {
if (!vis[i]) {
dep[i] = 1;
dfs(i);
fa[i][0] = i;
w[i][0] = INF;
}
}
for (int i = 1; i <= 20; i++) {
for (int j = 1; j <= n; j++) {
fa[j][i] = fa[fa[j][i - 1]][i - 1];
w[j][i] = min(w[j][i - 1], w[fa[j][i - 1]][i - 1]);
}
}
int q;
cin >> q;
while (q--) {
int x, y;
cin >> x >> y;
cout << lca(x, y) << endl;
}
return 0;
}
图论
最短路
//多源最短路 Floyd算法(O(n^3))
//本质上是一个动态规划的思想,每一次循环更新经过前k个节点,i到j的最短路径。
int dis[400][400];
void floyd(int n)
{
for (int k = 1; k <= n; k++)
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
}
//Floyd初始化
memset(dis, 0x3f, sizeof(dis));
//利用memset的特性,先把所有距离初始化为0x3f3f3f3f,注意这个数的两倍小于32位和64位机器上的INT_MAX
for (int i = 1; i <= n; ++i)
dis[i][i] = 0;
for (int i = 0; i < m; ++i)
{
int u, v, w;
cin >> u >> v >> w;
dis[u][v] = w;
}
//____________________________________________________________________________________________________________________________
// 单源最短路
// 1. Bellman-Ford算法(O(m*n)) 处理负权边
// 把所有边松弛 n - 1 遍
int dis[N], backup[N];
void Bellman_Ford() {
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
for (int j = 0; j < n - 1; ++j) {
memcpy(backup, dis, sizeof(dis));
for (int i = 1; i <= m; ++i)
dis[edges[i].to] = min(dis[edges[i].to], backup[edges[i].from] + edges[i].w);
}
}
//____________________________________________________________________________________________________________________________
// 2. SPFA算法
// 队列优化的Bellman-Ford算法
// 每次不松弛所有点,而只松弛可能更新的点?观察发现,第一次松弛S,P1时,可能更新的点只可能是S能直接到达的点。然后下一次可能被更新的则是S能直接到达的点能直接到达的点
// 时间复杂度不稳定,最坏情况可以被卡成Bellman-Ford
// SPFA也可以判负权环,我们可以用一个数组记录每个顶点进队的次数,当一个顶点进队超过n - 1次时,就说明存在负权
void spfa() {
queue<int> q;
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
q.push(1);
while (!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for (int i = 0; i < to[u].size(); ++i) { //遍历邻接的顶点
int v = to[u][i], w = edge[u][i];
if (dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
if (!vis[v]) {
vis[v] = true;
q.push(v);
}
}
}
}
}
//____________________________________________________________________________________________________________________________
// 3. Dijkstra算法
//Dij基于一种贪心的思想,处理一张没有负边的图
// 朴素O(n^2), 堆优化O(mlogn)
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int maxn = 110, maxm = 410, INF = 0x3f3f3f3f;
int n, m, g[maxn][maxn], dis[maxn];
bool vis[maxn];
void dijkstra() {
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
for (int i = 1; i <= n; i++) {
int t = -1;
for (int j = 1; j <= n; j++) {
if (!vis[j] && (t == -1 || dis[j] < dis[t])) {
t = j;
}
}
vis[t] = true;
for (int j = 1; j <= n; j++) {
dis[j] = min(dis[j], dis[t] + g[t][j]);
}
}
}
// 堆优化
#include <iostream>
#include <queue>
#include <cstring>
using namespace std;
using PII = pair<int, int>;
const int maxn = 2510, maxm = 6210 * 2;
int h[maxn], e[maxm], ne[maxm], w[maxm], idx;
int dis[maxn];
bool vis[maxn];
int n, m, s, t;
void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
void dijkstra() {
memset(dis, 0x3f, sizeof(dis));
memset(vis, 0, sizeof(vis));
priority_queue<PII, vector<PII>, greater<PII>> q;
q.push({0, s});
dis[s] = 0;
while (q.size()) {
PII tmp = q.top();
q.pop();
if (vis[tmp.second]) continue;
vis[tmp.second] = true;
for (int i = h[tmp.second]; ~i; i = ne[i]) {
int j = e[i];
if (dis[j] > tmp.first + w[i]) {
dis[j] = tmp.first + w[i];
q.push({dis[j], j});
}
}
}
}
最小生成树
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 110;
int g[maxn][maxn], dis[maxn];
bool vis[maxn];
int n;
int prim() {
int res = 0;
memset(dis, 0x3f, sizeof(dis));
dis[1] = 0;
for (int i = 1; i <= n; i++) {
int t = -1;
for (int j = 1; j <= n; j++) {
if (!vis[j] && (t == -1 || dis[t] > dis[j])) {
t = j;
}
}
vis[t] = true;
res += dis[t];
for (int j = 1; j <= n; j++) {
dis[j] = min(dis[j], g[t][j]);
}
}
return res;
}
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 110, maxm = 210;
int p[maxn], n, m;
struct Edge {
int a, b, w;
bool operator < (const Edge& oth) const {
return w < oth.w;
}
}edge[maxm];
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) p[i] = i;
for (int i = 0; i < m; i++) {
int a, b, w;
cin >> a >> b >> w;
edge[i] = {a, b, w};
}
sort(edge, edge + m);
int res = 0;
for (int i = 0; i < m; i++) {
int a = find(edge[i].a), b = find(edge[i].b), w = edge[i].w;
if (a != b) p[a] = b;
else res += w;
}
cout << res << endl;
return 0;
}
// 严格次小生成树
#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
using ll = long long;
const int maxn = 510, maxm = 1e4 + 10;
int p[maxn], n, m;
int h[maxn], e[maxn * 2], ne[maxn * 2], w[maxn * 2], idx;
int dis1[maxn][maxn], dis2[maxn][maxn];
struct Edge {
int a, b, w;
bool flag = false;
bool operator < (const Edge& oth) const {
return w < oth.w;
}
}edges[maxm];
void add(int a, int b, int c) {
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx++;
}
int find(int x) {
if (p[x] != x) return p[x] = find(p[x]);
return p[x];
}
void dfs(int u, int fa, int maxd1, int maxd2, int d1[], int d2[]) {
d1[u] = maxd1, d2[u] = maxd2;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
int td1 = maxd1, td2 = maxd2;
if (w[i] > td1) td2 = td1, td1 = w[i];
else if (w[i] < td1 && w[i] > td2) td2 = w[i];
dfs(j, u, td1, td2, d1, d2);
}
}
int main() {
memset(h, -1, sizeof(h));
cin >> n >> m;
for (int i = 0; i < m; i++) {
int a, b, w;
cin >> a >> b >> w;
edges[i] = { a, b, w };
}
sort(edges, edges + m);
for (int i = 1; i <= n; i++) p[i] = i;
ll sum = 0;
for (int i = 0; i < m; i++) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
int pa = find(a), pb = find(b);
if (pa != pb) {
sum += w;
p[pa] = pb;
add(a, b, w);
add(b, a, w);
edges[i].flag = true;
}
}
for (int i = 1; i <= n; i++) dfs(i, -1, -1e9, -1e9, dis1[i], dis2[i]);
ll res = 1e18;
for (int i = 0; i < m; i++) {
int a = edges[i].a, b = edges[i].b, w = edges[i].w;
if (edges[i].flag) continue;
ll tmp = 1e18;
if (w > dis1[a][b]) {
tmp = sum + w - dis1[a][b];
}
else if (w > dis2[a][b]) {
tmp = sum + w - dis2[a][b];
}
res = min(res, tmp);
}
cout << res << endl;
return 0;
}
LCA
一组节点的LCA是DFS序最小和最大的两点的LCA
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 4e4 + 10, maxm = maxn * 2;
int h[maxn], e[maxm], ne[maxm], idx;
int dep[maxn], fa[maxn][16], q[maxn];
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void bfs(int root) {
memset(dep, 0x3f, sizeof(dep));
dep[0] = 0, dep[root] = 1;
int hh = 0, tt = 0;
q[0] = root;
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (dep[j] > dep[t] + 1) {
dep[j] = dep[t] + 1;
q[++tt] = j;
fa[j][0] = t;
for (int k = 1; k <= 15; k++) {
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
}
int lca(int a, int b) {
if (dep[a] < dep[b]) swap(a, b);
for (int k = 15; k >= 0; k--) {
if (dep[fa[a][k]] >= dep[b]) {
a = fa[a][k];
}
}
if (a == b) return a;
for (int k = 15; k >= 0; k--) {
if (fa[a][k] != fa[b][k]) {
a = fa[a][k];
b = fa[b][k];
}
}
return fa[a][0];
}
int main() {
int n;
cin >> n;
memset(h, -1, sizeof(h));
int root;
for (int i = 1; i <= n; i++) {
int a, b;
cin >> a >> b;
if (b == -1) root = a;
else add(a, b), add(b, a);
}
bfs(root);
int m;
cin >> m;
while (m--) {
int a, b;
cin >> a >> b;
int p = lca(a, b);
if (p == a) cout << 1 << endl;
else if (p == b) cout << 2 << endl;
else cout << 0 << endl;
}
return 0;
}
匈牙利算法
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 100010, M = 200010;
int n, m;
int h[N], e[M], ne[M], idx;
int color[N];
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx;
idx++;
}
// 染色法判断二分图
bool dfs(int u, int c) {
color[u] = c;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (!color[j]) {
if (!dfs(j, 3 - c)) return false;
}
else if (color[j] == c) return false;
}
return true;
}
bool st[N];
int match[N];
bool find(int x) {
for (int i = h[x]; i != -1; i = ne[i]) {
int j = e[i];
if (!st[j]) {
st[j] = true;
if (match[j] == 0 || find(match[j])) {
match[j] = x;
return true;
}
}
}
return false;
}
int main() {
cin >> n >> m;
memset(h, -1, sizeof(h));
while (m--) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
// 判断
bool flag = true;
for (int i = 1; i <= n; i++) {
if (!color[i]) {
if (!dfs(i, 1)) {
flag = false;
break;
}
}
}
if (flag) cout << "Yes" << endl;
else cout << "No" << endl;
// 匈牙利
int res = 0;
for (int i = 1; i <= n; i++) {
memset(st, false, sizeof(st));
if (find(i)) res++;
}
cout << res << endl;
}
无向图求环
vector<int> g[maxn], cycle;
bool v[maxn], st[maxn], oncycle[maxn];
int fa[maxn];
void dfs(int u, int st) {
if (cycle.size()) return;
v[u] = 1;
for (auto j : g[u]) {
if (j == fa[u]) continue;
if (cycle.size()) return;
if (j == st) {
int t = u;
while (t != j) {
cycle.push_back(t);
oncycle[t] = 1;
t = fa[t];
}
cycle.push_back(j);
oncycle[j] = 1;
reverse(cycle.begin(), cycle.end());
return;
}
if (!v[j]) {
fa[j] = u;
dfs(j, st);
}
}
}
网络流
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 10010, maxm = 200010, INF = 1e9;
int n, m, S, T;
int h[maxn], e[maxm], f[maxm], ne[maxm], idx;
int q[maxn], d[maxn], cur[maxn];
void add(int a, int b, int c) {
e[idx] = b, f[idx] = c, ne[idx] = h[a], h[a] = idx++;
e[idx] = a, f[idx] = 0, ne[idx] = h[b], h[b] = idx++;
}
bool bfs() {
int hh = 0, tt = 0;
memset(d, -1, sizeof(d));
q[0] = S, d[S] = 0, cur[S] = h[S];
while (hh <= tt) {
int t = q[hh++];
for (int i = h[t]; ~i; i = ne[i]) {
int v = e[i];
if (d[v] == -1 && f[i]) {
d[v] = d[t] + 1;
cur[v] = h[v];
if (v == T) return true;
q[++tt] = v;
}
}
}
return false;
}
int find(int u, int limit) {
if (u == T) return limit;
int flow = 0;
for (int i = cur[u]; ~i && flow < limit; i = ne[i]) {
cur[u] = i;
int v = e[i];
if (d[v] == d[u] + 1 && f[i]) {
int t = find(v, min(f[i], limit - flow));
if (!t) d[v] = -1;
f[i] -= t, f[i ^ 1] += t, flow += t;
}
}
return flow;
}
int dinic() {
int res = 0, flow;
while (bfs()) {
while (flow = find(S, INF)) {
res += flow;
}
}
return res;
}
int main() {
cin >> n >> m >> S >> T;
memset(h, -1, sizeof(h));
while (m--) {
int a, b, c;
cin >> a >> b >> c;
add(a, b, c);
}
cout << dinic() << endl;
return 0;
}
有源上下界最大/小流
创建虚拟源点S和T补流, 又因为原图有自带的源汇点(s, t), 因此源汇点不保证流量守恒(其他店保证),所以包补一条t到s的满容量边让远点流量守恒
int main() {
int s, t;
cin >> n >> m >> s >> t;
S = 0, T = n + 1;
memset(h, -1, sizeof(h));
while (m--) {
int a, b, c, d;
cin >> a >> b >> c >> d;
add(a, b, d - c);
A[a] -= c, A[b] += c;
}
int tot = 0;
for (int i = 1; i <= n; i++) {
if (A[i] > 0) add(S, i, A[i]), tot += A[i];
else if (A[i] < 0) add(i, T, -A[i]);
}
add(t, s, INF);
if (dinic() < tot) {
cout << "No Solution" << endl;
return 0;
}
S = s, T = t;
int res = f[idx - 1];
f[idx - 1] = f[idx - 2] = 0;
cout << res + dinic() << endl;
return 0;
}
最大权闭合图
新建一个S点对于权值>0的点连边,新建一个T点对于权值<0的点连边(p,t)。结论是:最大权闭合子图的值=所有的正权值点和-最小割。
int main() {
cin >> n >> m;
S = 0, T = n + m + 1;
memset(h, -1, sizeof(h));
for (int i = 1; i <= n; i++) {
int x;
cin >> x;
add(m + i, T, x);
}
int sum = 0;
for (int i = 1; i <= m; i++) {
int a, b, c;
cin >> a >> b >> c;
add(S, i, c);
add(i, m + a, INF);
add(i, m + b, INF);
sum += c;
}
cout << sum - dinic() << endl;
return 0;
}
最大密度子图
最大化边的数量/点的数量
费用流
template<typename cost_t>
struct MinCostMaxFlow {
const cost_t INF = numeric_limits<cost_t>::max() / 2;
int h[maxn], e[maxm], ne[maxm], idx;
cost_t f[maxm], w[maxm], d[maxn], incf[maxn];
int q[maxn], pre[maxn];
bool vis[maxn];
int V, S, T;
void init(int v, int s, int t) {
for(int i = 0; i <= v; i++) h[i] = -1;
idx = 0;
V = v, S = s, T = t;
}
void add(int a, int b, cost_t c, cost_t d) {
e[idx] = b, f[idx] = c, w[idx] = d, ne[idx] = h[a], h[a] = idx++;
e[idx] = a, f[idx] = 0, w[idx] = -d, ne[idx] = h[b], h[b] = idx++;
}
bool spfa() {
int hh = 0, tt = 0;
for(int i = 0; i <= V; i++){
d[i] = INF;
incf[i] = 0;
vis[i] = 0;
}
q[tt++] = S, d[S] = 0, incf[S] = INF;
while(hh != tt) {
int t = q[hh++];
if (hh == maxn) hh = 0;
vis[t] = 0;
for(int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (f[i] && d[j] > d[t] + w[i]){
d[j] = d[t] + w[i];
incf[j] = min(incf[t], f[i]);
pre[j] = i;
if (!vis[j]){
vis[j] = 1;
q[tt++] = j;
if (tt == maxn) tt = 0;
}
}
}
}
return incf[T] > 0;
}
pair<cost_t, cost_t> EK() {
cost_t flow = 0, cost = 0;
while(spfa()){
cost_t t = incf[T];
flow += t, cost += d[T] * t;
for(int i = T; i != S; i = e[pre[i] ^ 1]) {
f[pre[i]] -= t, f[pre[i] ^ 1] += t;
}
}
return {flow, cost};
}
};
MinCostMaxFlow<int> flow;
2-Sat
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 2e6 + 10, maxm = 2e6 + 10;
int n, m;
int h[maxn], e[maxm], ne[maxm], idx;
int dfn[maxn], low[maxn], ts, stk[maxn], top;
int id[maxn], cnt;
bool ins[maxn];
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void tarjan(int u) {
dfn[u] = low[u] = ++ts;
stk[++top] = u, ins[u] = true;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (!dfn[j]) {
tarjan(j);
low[u] = min(low[u], low[j]);
}
else if (ins[j]) low[u] = min(low[u], dfn[j]);
}
if (low[u] == dfn[u]) {
int y;
cnt++;
do {
y = stk[top--], ins[y] = false, id[y] = cnt;
} while (y != u);
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n >> m;
memset(h, -1, sizeof(h));
while (m--) {
int i, a, j, b;
cin >> i >> a >> j >> b;
i--, j--;
add(2 * i + !a, 2 * j + b);
add(2 * j + !b, 2 * i + a);
}
for (int i = 0; i < 2 * n; i++) {
if (!dfn[i]) tarjan(i);
}
for (int i = 0; i < n; i++) {
if (id[2 * i] == id[2 * i + 1]) {
cout << "IMPOSSIBLE" << endl;
return 0;
}
}
cout << "POSSIBLE" << endl;
for (int i = 0; i < n; i++) {
if (id[2 * i] < id[2 * i + 1]) cout << "0 ";
else cout << "1 ";
}
return 0;
}
struct SCC{
int h[maxn], hn[maxn], e[maxm], ne[maxm], idx;
int dfn[maxn], low[maxn], ts;
int stk[maxn], top;
int scc_cnt, id[maxn];
vector<int> scc[maxn];
int n;
bool ins[maxn];
void add(int h[], int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void add(int a, int b){
add(h, a, b);
}
void tarjan(int u){
dfn[u] = low[u] = ++ts;
stk[++top] = u;
ins[u] = 1;
for(int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if (!dfn[j]){
tarjan(j);
low[u] = min(low[u], low[j]);
}
else if (ins[j]) low[u] = min(low[u], dfn[j]);
}
if (dfn[u] == low[u]){
++scc_cnt;
int y;
do{
y = stk[top--];
id[y] = scc_cnt;
ins[y] = 0;
scc[scc_cnt].push_back(y);
}while(y != u);
}
}
void init(int _n){
n = _n;
for(int i = 1; i <= n; i++){
scc[i].clear();
dfn[i] = 0;
h[i] = hn[i] = -1;
}
scc_cnt = top = ts = idx = 0;
}
void build(){
for(int i = 1; i <= n; i++)
if (!dfn[i]) tarjan(i);
for(int x = 1; x <= n; x++)
for(int i = h[x]; ~i; i = ne[i]){
int j = e[i];
if (id[j] != id[x]) add(hn, id[x], id[j]);
}
}
void topsort(){
for(int x = scc_cnt; x; x--){
}
}
}scc;
数学
线性筛
#include <iostream>
using namespace std;
const int maxn = 1e6 + 10;
int isprime[maxn], prime[maxn], cnt;
void init() {
isprime[1] = true;
for (int i = 2; i < maxn; i++) {
if (!isprime[i]) prime[cnt++] = i;
for (int j = 0; prime[j] <= maxn / i; j++) {
isprime[prime[j] * i] = true;
if (i % prime[j] == 0) break;
}
}
}
欧几里得
// 2. 欧几里得算法(或辗转相除法
//辗转相除法,求两个数的最大公因数
//gcd(m,n) = gcd(m + kn,n)
//gcd(a, gcd(b, c)) == gcd(gcd(a, b), c)
//O(log(min(m,n)))
int gcd(int a, int b)
{
if (b == 0)
return a;
else
return gcd(b, a % b);
}
// 拓展欧几里德 可以在辗转相除途中求出不定方程 ax + by = c 的一组解。
// 裴蜀定理 : 设 a,b 为正整数,则关于x,y的方程 ax + by = c有整数解当且仅当c是gcd(a,b)的倍数。
// x = x0 + kb / d, y = y0 + ka / d
int exgcd(int a, int b, int &x, int &y)// 拓欧
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
// 求逆元
// ab == 1(mod p) a = inv(b)
// a/b = a * inv(b)(mod p)
// ax + by = 1 a * x = 1(mod b)
// ax = 1 (mod b) 则x为a的逆元
// a ^ (b - 2) 要求b为素数
// exgcd(a, p, x, y)中的x 要求gcd(a, p) == 1
int inv(int a, int p)
{
int x, y;
if (exgcd(a, p, x, y) != 1) // 无解的情形
return -1;
return (x % p + p) % p;
}
中国剩余定理
/*
x % a[1] = m[1]
x % a[2] = m[2]
x % a[3] = m[3]
......
x % a[n] = m[n]
要求m[1] ... m[n]两两互质
*/
#include <iostream>
using namespace std;
using ll = long long;
ll exgcd(ll a, ll b, ll &x, ll &y) {
if (b == 0) {
x = 1;
y = 0;
return a;
}
ll d = exgcd(b, a % b, y, x);
y -= (a / b) * x;
return d;
}
ll inv(ll a, ll p) {
ll x, y;
exgcd(a, p, x, y);
return (x % p + p) % p;
}
inline ll CRT(ll m[], ll a[], ll n) {
ll M = 1, x = 0;
for (int i = 0; i < n; i++) M *= m[i];
for (int i = 0; i < n; i++) {
ll Mi = M / m[i];
x += (a[i] * Mi * inv(Mi, m[i])) % M;
}
return x % M;
}
组合数
// O(max(a, b) ^ 2)
// C(a, b) = C(a - 1, b) + C(a - 1, b - 1)
const int maxn = 2010;
int c[2010][2010];
const int mod = 1e9 + 7;
using ll = long long;
void init() {
for (int i = 0; i < maxn; i++) {
for (int j = 0; j < maxn; j++) {
if (!j) c[i][j] = 0;
else c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
}
// O(nlogn)
int fact[maxn],infact[maxn];
// 费马小定理 若p是质数且gcd(a,p) = 1, 则有 a ^ (p - 1) = 1 (mod p)
// 逆元inv(a) = a ^ (p - 2)(mod p)
ll ksm(int a, int b, int mod)
{
ll res = 1;
while(b)
{
if(b & 1) res = res * a % mod;
b >>= 1;
a = (ll)a * a % mod;
}
return res;
}
void init()
{
fact[0] = infact[0] = 1;
for(int i = 1; i < maxn; i++)
{
fact[i] = (ll)fact[i - 1] * i % mod;
infact[i] = (ll)infact[i - 1] * ksm(i, mod - 2, mod) % mod;
}
}
ll C(int a,int b) {
if(b > a || b < 0) return 0;
return (ll)fact[a] * infact[b] % mod * infact[a - b] % mod;
}
ll divide(int a, int b) {
if (a == 0 && b == 0) return 1; //挖个坑
return C(a - 1, b - 1);
}
// Lucas定理 O(mod)
// C(a, b) = C(a % mod, b % mod) * C(a / p, b / p) (mod p)
int C(int a, int b) {
int res = 1, inv = 1;
for (int i = 1, j = a; i <= b; i++, j--) {
res = (ll)res * j % mod;
inv = (ll)inv * i % mod;
}
res = (ll)res * ksm(inv, mod - 2) % mod;
return res;
}
int lucas(ll a, ll b) {
if (a < mod && b < mod) return C(a, b);
return (ll)C(a % mod, b % mod) * lucas(a / mod, b / mod) % mod;
}
// 卡特兰数 C(2n, n) / (n + 1)
获得所有因数
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
using PII = pair<int, int>;
const int maxn = 5e4 + 10;
int primes[maxn], isprime[maxn], cnt;
int divisor[1601], dcnt;
vector<PII> factor;
void init(int n) {
for (int i = 2; i <= n; i++) {
if (!isprime[i]) primes[cnt++] = i;
for (int j = 0; primes[j] <= n / i; j++) {
isprime[primes[j] * i] = true;
if (i % primes[j] == 0) break;
}
}
}
void dfs(int u, int num) {
if (u == factor.size()) {
divisor[dcnt++] = num;
return;
}
for (int i = 0; i <= factor[u].second; i++) {
dfs(u + 1, num);
num *= factor[u].first;
}
}
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
int main() {
init(maxn - 1);
int T;
cin >> T;
while (T--) {
factor.clear();
int a, b, c, d;
cin >> a >> b >> c >> d;
int tmp = d;
for (int i = 0; primes[i] <= tmp / primes[i]; i++) {
int p = primes[i];
if (tmp % p == 0) {
int num = 0;
while (tmp % p == 0) tmp /= p, num++;
factor.push_back({p, num});
}
}
if (tmp > 1) factor.push_back({tmp, 1});
dcnt = 0;
dfs(0, 1);
int res = 0;
for (int i = 0; i < dcnt; i++) {
int x = divisor[i];
if (gcd(a, x) == b && (ll)c * x / gcd(c, x) == d) res++;
}
cout << res << endl;
}
return 0;
}
欧拉函数
// n为质数, 欧拉函数为n - 1
// 欧拉函数是积性函数, 当gcd(a, b) = 1, φ(ab) = φ(a) * φ(b)
// 特别地, n为奇数时, φ(2n) = φ(n)
// n = Σ(d|n) φ(d)
// n = p ^ k(p为质数), φ(n) = p ^ k - p ^ (k - 1)
// n = ∏(i = 1, s)pi ^ ki, φ(n) = n * ∏(i = 1, s) (pi - 1) / pi;
// 欧拉定理 gcd(a, m) = 1, a ^ (φ(m)) = 1(mod m)
#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>
using namespace std;
const int maxn = 1e5 + 5;
int main() {
int n;
cin >> n;
int res = n;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
res = res / i * (i - 1);
while (n % i == 0) n /= i;
}
}
if (n > 1) res = res / n * (n - 1);
cout << res << endl;
}
#include <iostream>
using namespace std;
const int maxn = 1010;
int isprime[maxn], primes[maxn], cnt, phi[maxn];
void init(int n) {
isprime[1] = 1;
phi[1] = 1;
for (int i = 2; i <= n; i++) {
if (!isprime[i]) primes[cnt++] = i, phi[i] = i - 1;
for (int j = 0; primes[j] <= n / i; j++) {
isprime[primes[j] * i] = true;
if (i % primes[j] == 0) {
phi[primes[j] * i] = primes[j] * phi[i];
break;
}
phi[primes[j] * i] = (primes[j] - 1) * phi[i];
}
}
}
矩阵乘法
#include <iostream>
#include <cstring>
using namespace std;
using ll = long long;
const int maxn = 25;
int n, m, mod;
char s[maxn];
int nxt[maxn], a[maxn][maxn];
void qmul(int a[][maxn], int b[][maxn], int c[][maxn]) {
static int t[maxn][maxn];
memset(t, 0, sizeof(t));
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
for (int k = 0; k < m; k++) {
t[i][j] = (t[i][j] + (ll)a[i][k] * b[k][j] % mod) % mod;
}
}
}
memcpy(c, t, sizeof(t));
}
int main() {
cin >> n >> m >> mod >> s + 1;
for (int i = 2, j = 0; i <= m; i++) {
while (j && s[j + 1] != s[i]) j = nxt[j];
if (s[j + 1] == s[i]) j++;
nxt[i] = j;
}
for (int j = 0; j < m; j++) {
for (int c = '0'; c <= '9'; c++) {
int k = j;
while (k && s[k + 1] != c) k = nxt[k];
if (s[k + 1] == c) k++;
if (k < m) a[j][k]++;
}
}
int f[maxn][maxn] = {1};
while (n) {
if (n & 1) qmul(f, a, f);
qmul(a, a, a);
n >>= 1;
}
int res = 0;
for (int i = 0; i < m; i++) res = (res + f[0][i]) % mod;
cout << res << endl;
return 0;
}
高斯消元
// O(n ^ 3) 求解包含n个方程和n个未知数多元线性方程组
#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
const int N = 110;
const double eps = 1e-6;
double a[N][N];
int n;
// 0 唯一解 1 无穷多解 2 无解
int gauss() {
int c, r;
for (c = 0, r = 0; c < n; c++, r++) {
int t = r;
for (int i = r; i < n; i++) {
if (fabs(a[i][c]) > fabs(a[t][c])) t = i;
}
if (fabs(a[t][c]) < eps) break;
for (int i = c; i <= n; i++) swap(a[t][i], a[r][i]);
for (int i = n; i >= c; i--) a[r][i] /= a[r][c];
for (int i = r + 1; i < n; i++) {
if (fabs(a[i][c]) > eps) {
for (int j = n; j >= c; j--) {
a[i][j] -= a[r][j] * a[i][c];
}
}
}
}
if (r < n) {
for (int i = r; i < n; i++) {
if (fabs(a[i][n]) > eps) return 2;
}
return 1;
}
for (int i = n - 1; i >= 0; i--) {
for (int j = i + 1; j < n; j++) {
a[i][n] -= a[i][j] * a[j][n];
}
}
return 0;
}
// 开关异或问题
int gauss()
{
int r, c;
for (r = 1, c = 1; c <= n; c ++ )
{
// 找主元
int t = r;
for (int i = r + 1; i <= n; i ++ )
if (a[i][c])
t = i;
if (!a[t][c]) continue;
// 交换
for (int i = c; i <= n + 1; i ++ ) swap(a[t][i], a[r][i]);
// 消
for (int i = r + 1; i <= n; i ++ )
for (int j = n + 1; j >= c; j -- )
a[i][j] ^= a[i][c] & a[r][j];
r ++ ;
}
int res = 1;
if (r < n + 1)
{
for (int i = r; i <= n; i ++ )
{
if (a[i][n + 1]) return -1; // 出现了 0 == !0,无解
res *= 2;
}
}
return res;
}
容斥原理
// 时间复杂度 O(2 ^ n)
#include <iostream>
#include <vector>
using ll = long long;
using namespace std;
vector<int> divisor;
void get_factor(int k) // 对 k 进行分解质因数
{
divisor.clear();
for (int i = 2; i <= k / i; i ++)
{
if (k % i == 0)
{
divisor.push_back(i);
while(k % i == 0) k /= i;
}
}
if (k > 1) divisor.push_back(k);
}
int count(int limit)
{
int res = 0;
for (int i = 1; i < (1 << divisor.size()); i ++)
{
ll x = 1, sum = 0;
for (int j = 0; j < divisor.size(); j ++)
{
if ((i >> j) & 1) x *= divisor[j], sum ++;
}
if (sum & 1) res += limit / x;
else res -= limit / x;
}
return limit - res; // 利用容斥求出来的是 1 ~ limit中 与 k 不互质的个数, 返回互质的数量
}
// 同理利用 count 也可以求 l ~ r 区间内 与 某一数 x 互质的个数,或者不互质的个数
// 即 count(r) - count(l - 1) , 前缀和
博弈论
#include <iostream>
#include <algorithm>
#include <unordered_set>
#include <cstring>
using namespace std;
const int maxn = 110, maxm = 10010;
int n, m;
int s[maxn], f[maxm];
int sg(int x) {
if (f[x] != -1) return f[x];
unordered_set<int> S;
for (int i = 0; i < m; i++) {
int sum = s[i];
if (x >= sum) S.insert(sg(x - sum));
}
for (int i = 0; ; i++) {
if (!S.count(i)) {
return f[x] = i;
}
}
}
int main() {
cin >> m;
for (int i = 0; i < m; i++) cin >> s[i];
cin >> n;
memset(f, -1, sizeof(f));
int res = 0;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
res ^= sg(x);
}
if (res) cout << "Yes" << endl;
else cout << "No" << endl;
return 0;
}
NTT
#include<iostream>
#include<cstring>
#include<vector>
#include<random>
using namespace std;
using LL = long long;
const int maxn = 2e6 + 5, mod = 998244353, G = 3, Gi = 332748118;
int qpow(int a, int b, int mod){
int res = 1;
while (b){
if (b & 1) res = 1LL * res * a % mod;
a = 1LL * a * a % mod;
b >>= 1;
}
return res;
}
inline int mul(int a, int b){
return 1LL * a * b % mod;
}
inline void add(int &a, int b){
a += b;
if (a >= mod) a -= mod;
}
inline void sub(int &a, int b){
a -= b;
if (a < 0) a += mod;
}
namespace NTT{
vector<int> Omega(int L){
int wn = qpow(G, mod / L, mod);
vector<int> w(L);
w[L >> 1] = 1;
for(int i = L / 2 + 1; i < L; i++) w[i] = mul(w[i - 1], wn);
for(int i = L / 2 - 1; i >= 1; i--) w[i] = w[i << 1];
return w;
}
auto W = Omega(1 << 21);
void DIF(int *a, int n) {
for(int k = n >> 1; k; k >>= 1)
for(int i = 0, y; i < n; i += k << 1)
for(int j = 0; j < k; j++){
y = a[i + j + k], a[i + j + k] = mul(a[i + j] - y + mod, W[k + j]),
add(a[i + j], y);
}
}
void IDIT(int *a, int n) {
for (int k = 1; k < n; k <<= 1)
for (int i = 0, x, y; i < n; i += k << 1)
for(int j = 0; j < k; j++){
x = a[i + j], y = mul(a[i + j + k], W[k + j]),
a[i + j + k] = x - y < 0 ? x - y + mod : x - y;
add(a[i + j], y);
}
int inv = mod - (mod - 1) / n;
for(int i = 0; i < n; i++) a[i] = mul(a[i], inv);
reverse(a + 1, a + n);
}
}
using Poly = std::vector<int>;
void DFT(Poly &a){
NTT::DIF(a.data(), a.size());
}
void IDFT(Poly &a){
NTT::IDIT(a.data(), a.size());
}
int norm(int n) {
return n == 1 ? 1 : (1 << (32 - __builtin_clz(n - 1)));
}
void norm(Poly &a) {
if (!a.empty()) a.resize(norm(a.size()), 0);
}
void dot(Poly &a, Poly &b) {
for(int i = 0; i < a.size(); i++)
a[i] = mul(a[i], b[i]);
}
Poly get(Poly A, Poly B){
int n = A.size() + B.size() - 1, L = norm(n);
A.resize(L), B.resize(L);
vector<int> a(L), b(L), p(L);
for(int i = 0; i < L; i++)
a[i] = A[i] * A[i] * A[i], b[i] = B[i];
DFT(a), DFT(b);
dot(a, b);
for(int i = 0; i < L; i++) add(p[i], a[i]);
for(int i = 0; i < L; i++)
a[i] = A[i], b[i] = B[i] * B[i] * B[i];
DFT(a), DFT(b);
dot(a, b);
for(int i = 0; i < L; i++) add(p[i], a[i]);
for(int i = 0; i < L; i++)
a[i] = A[i] * A[i], b[i] = B[i] * B[i];
DFT(a), DFT(b);
dot(a, b);
for(int i = 0; i < L; i++) sub(p[i], mul(a[i], 2));
IDFT(p);
p.resize(n);
return p;
}
Poly operator*(Poly a, Poly b) {
int n = a.size() + b.size() - 1, L = norm(n);
if (a.size() <= 8 || b.size() <= 8) {
Poly c(n);
for(int i = 0; i < a.size(); i++)
for(int j = 0; j < b.size(); j++)
c[i + j] = (c[i + j] + 1LL * a[i] * b[j]) % mod;
return c;
}
a.resize(L), b.resize(L);
DFT(a), DFT(b), dot(a, b), IDFT(a);
return a.resize(n), a;
}
// 分治FFT
Poly MulAll(int l, int r, vector<Poly> &a){
if (l == r) return a[r];
int mid = (l + r) / 2;
return MulAll(l, mid, a) * MulAll(mid + 1, r, a);
}
/*
vector<Poly> a(n);
for (int i = 0; i < n; i++) {
a[i] = {1, d[i] - (i > 0)};
}
auto b = MulAll(0, n - 1, a);
*/
动态规划
背包
// 01 背包 (每件物品最多只能用一次)
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int n, m;
int v[maxn], w[maxn];
int f[maxn];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> v[i] >> w[i];
for (int i = 1; i <= n; i++) {
for (int j = m; j >= v[i]; j--) {
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
}
cout << f[m] << endl;
return 0;
}
// 完全背包 (每件物品无限次) for (int j = v[i]; j <= m; j++)
// 整数划分 (计数dp)
f[0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
f[j] = f[j] + f[j - i];
}
}
cout << f[n] << endl;
// 多重背包 (每个物品最多用x[i]次)
// f[i][j] = max(f[i - 1][j - v[i] * k] + w[i] * k) (k = 0, 1, 2, ..., s[i])
// s[i]->logs[i] 二进制优化O(mnlog(s))
int cnt = 0;
for (int i = 1; i <= n; i++) {
int a, b, s;
cin >> a >> b >> s;
int k = 1;
while (k <= s) {
cnt++;
v[cnt] = a * k;
w[cnt] = b * k;
s -= k;
k *= 2;
}
if (s > 0) {
cnt++;
v[cnt] = a * s;
w[cnt] = b * s;
}
}
n = cnt;
for (int i = 1; i <= n; i++) {
for (int j = m; j >= v[i]; j--) {
f[j] = max(f[j], f[j - v[i]] + w[i]);
}
}
cout << f[m] << endl;
return 0;
// 分组背包 (n种每种有s[i]个, 1组最多1个)
int v[maxn][maxn], w[maxn][maxn];
int f[maxn], s[maxn];
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> s[i];
for (int j = 0; j < s[i]; j++) {
cin >> v[i][j] >> w[i][j];
}
}
for (int i = 1; i <= n; i++) {
for (int j = m; j >= 0; j--) {
for (int k = 0; k < s[i]; k++) {
if (v[i][k] <= j) {
f[j] = max(f[j], f[j - v[i][k]] + w[i][k]);
}
}
}
}
cout << f[m] << endl;
}
数位DP
#include <bits/stdc++.h>
using namespace std;
const int maxn = 35;
int f[maxn][10][110], mod;
void init() {
memset(f, 0, sizeof(f));
for (int i = 0; i < 10; i++) f[1][i][i % mod] = 1;
for (int i = 2; i < maxn; i++) {
for (int j = 0; j < 10; j++) {
for (int k = 0; k < mod; k++) {
for (int x = 0; x < 10; x++) {
f[i][j][k] += f[i - 1][x][((k - j) % mod + mod) % mod];
}
}
}
}
}
int dp(int n) {
if (!n) return 1;
vector<int> nums;
while (n) nums.push_back(n % 10), n /= 10;
int res = 0;
int last = 0;
for (int i = nums.size() - 1; i >= 0; i--) {
int x = nums[i];
for (int j = 0; j < x; j++) {
res += f[i + 1][j][(-last % mod + mod) % mod];
}
last += x;
if (!i && last % mod == 0) res++;
}
return res;
}
int main() {
int l, r;
while (cin >> l >> r >> mod) {
init();
cout << dp(r) - dp(l - 1) << endl;
}
return 0;
}
斜率优化DP
// k为和i有关, x和y和j有关
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int maxn = 3e5 + 10;
ll f[maxn], a[maxn], b[maxn], q[maxn];
int main() {
int n, s;
cin >> n >> s;
for (int i = 1; i <= n; i++) {
cin >> a[i] >> b[i];
a[i] += a[i - 1];
b[i] += b[i - 1];
}
int hh = 0, tt = 0;
for (int i = 1; i <= n; i++) {
int l = hh, r = tt;
while (l < r) {
int mid = l + r >> 1;
if (mid < tt && f[q[mid + 1]] - f[q[mid]] > (a[i] + s) * (b[q[mid + 1]] - b[q[mid]])) r = mid;
else l = mid + 1;
}
f[i] = f[q[r]] + s * (b[n] - b[q[r]]) + a[i] * (b[i] - b[q[r]]);
while (hh < tt && (double)(f[q[tt]] - f[q[tt - 1]]) * (b[i] - b[q[tt]]) >= (double)(f[i] - f[q[tt]]) * (b[q[tt]] - b[q[tt - 1]])) tt--;
q[++tt] = i;
}
cout << f[n] << endl;
return 0;
}
动态DP
用线段树维护矩阵信息,支持单点修改和计算结果(query(1, l, r));
#include <iostream>
#include <cstring>
#include <iomanip>
#include <functional>
#include <cmath>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
using namespace std;
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0)
#define endl '\n'
using ll = long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;
const int maxn = 2e5 + 5;
const int maxm = 2;
const ll INF = 1e18;
const int mod = 1e9 + 7;
int n, m, k;
int a[maxn];
struct matrix {
ll v[maxm][maxm];
matrix() {
for (int i = 0; i < maxm; i++) {
for (int j = 0; j < maxm; j++) {
v[i][j] = INF;
}
}
}
matrix operator * (const matrix& oth) const {
matrix res;
for (int i = 0; i < maxm; i++) {
for (int j = 0; j < maxm; j++) {
for (int k = 0; k < maxm; k++) {
res.v[i][j] = min(res.v[i][j], v[i][k] + oth.v[k][j]);
}
}
}
return res;
}
};
struct Node {
int l, r;
matrix mt;
}tr[maxn * 4];
void pushup(matrix &root, matrix &l, matrix &r) {
root = l * r;
}
void pushup(int u) {
pushup(tr[u].mt, tr[u << 1].mt, tr[u << 1 | 1].mt);
}
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
auto &v = tr[u].mt.v;
v[0][0] = v[0][1] = a[r];
v[1][0] = 0;
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void update(int u, int pos, int val) {
if (tr[u].l == pos && tr[u].r == pos) {
auto &v = tr[u].mt.v;
v[0][0] = v[0][1] = val;
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if (pos <= mid) update(u << 1, pos, val);
else update(u << 1 | 1, pos, val);
pushup(u);
}
matrix query(int u, int l, int r) {
if (l <= tr[u].l && tr[u].r <= r) return tr[u].mt;
int mid = tr[u].l + tr[u].r >> 1;
if (r <= mid) return query(u << 1, l, r);
if (l > mid) return query(u << 1 | 1, l, r);
matrix res;
matrix lm = query(u << 1, l, r);
matrix rm = query(u << 1 | 1, l, r);
pushup(res, lm, rm);
return res;
}
void Jared_McDs() {
cin >> n;
for (int i = 1; i < n; i++) cin >> a[i];
build(1, 1, n - 1);
int q;
cin >> q;
while (q--) {
int pos, val;
cin >> pos >> val;
update(1, pos, val);
cout << query(1, 1, n - 1).v[0][1] * 2ll << endl;
}
}
int main() {
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
IOS;
int w_ = 1;
//cin >> w_;
while (w_--) {
Jared_McDs();
}
return 0;
}
bitset优化
bitset优化要保证先找到拓扑序(排序)并且按拓扑序进行
最后更新答案要保证每条拓扑链的循环顺序大小要等价于逆拓扑序
## CF 1826E
#include <bits/stdc++.h>
using namespace std;
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0)
#define endl '\n'
using ll = long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;
const int maxn = 5010;
const int maxm = 2;
const ll INF = 1e18;
const int mod = 1e9 + 7;
int n, m, k, c;
void Jared_McDs() {
cin >> m >> n;
vector<ll> p(n);
for (int i = 0; i < n; i++) cin >> p[i];
vector<vector<int>> a(m, vector<int> (n));
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) cin >> a[i][j];
}
vector<int> index(n);
for (int i = 0; i < n; i++) index[i] = i;
vector<bitset<5000>> can(n);
for (int i = 0; i < n; i++) can[i].set();
for (int d = 0; d < m; d++) {
sort(index.begin(), index.end(), [&](int j, int k) {
return a[d][j] < a[d][k];
});
bitset<5000> tmp;
for (int i = 0; i < n; ) {
int j = i;
while (j < n && a[d][index[j]] == a[d][index[i]]) {
can[index[j]] &= tmp;
j++;
}
while (i < j) {
tmp[index[i]] = true;
i++;
}
}
}
vector<ll> dp = p;
for (int i : index) {
for (int j = 0; j < n; j++) {
if (can[i][j]) {
dp[i] = max(dp[i], dp[j] + p[i]);
}
}
}
cout << *max_element(dp.begin(), dp.end()) << endl;
}
int main() {
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
IOS;
int w_ = 1;
// cin >> w_;
while (w_--) {
Jared_McDs();
}
return 0;
}
状态压缩DP
一般是求最值,相同序列不同次序结果不同
#include <bits/stdc++.h>
using namespace std;
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0)
#define endl '\n'
using ll = long long;
using ULL = unsigned long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;
const int maxn = 1010;
const int maxm = 1e6 + 10;
const ll INF = 1e18;
const int mod = 998244353;
const double eps = 1e-8;
int n, m, k, q;
int smin[25], sum[25], bitsum[1 << 21], dp[1 << 21];
unordered_map<int, int> mp[25], up[25];
void WTCh() {
cin >> n;
vector<string> s(n + 1);
for (int i = 1; i <= n; i++) cin >> s[i], s[i] = '?' + s[i];
for (int i = 1; i <= n; i++) {
int val = 0;
for (int j = 1; j < s[i].size(); j++) {
val += (s[i][j] == '(' ? 1 : -1);
smin[i] = min(smin[i], val);
mp[i][val]++;
if (mp[i][val] == 1) up[i][val] = mp[i][val + 1];
}
sum[i] = val;
}
for (int bit = 0; bit < 1 << n; bit++) {
for (int i = 1; i <= n; i++) {
if ((bit >> i - 1) & 1) bitsum[bit] += sum[i];
}
}
vector<int> ok(1 << n, 0); // 是否有最小值小于0
ok[0] = 1;
int res = 0;
for (int bit = 0; bit < 1 << n; bit++) {
if (!ok[bit]) continue;
for (int i = 1; i <= n; i++) {
if (!((bit >> i - 1) & 1)) {
int nxt = bit | (1 << i - 1);
if (bitsum[bit] + smin[i] >= 0) dp[nxt] = max(dp[nxt], dp[bit] + mp[i][-bitsum[bit]]), ok[nxt] = 1;
else res = max(res, dp[bit] + up[i][-bitsum[bit] - 1]);
}
}
}
for (int bit = 0; bit < 1 << n; bit++) res = max(res, dp[bit]);
cout << res << endl;
}
int main() {
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
IOS;
int w_ = 1;
//cin >> w_;
while (w_--) {
WTCh();
}
return 0;
}
树形DP
按照以下方式解释合并:枚举已处理子级的所有子树内的顶点和新子级的子树内的顶部。迭代到子树的大小与遍历子树中的顶点的移动次数相同。合并将遍历所有的顶点对,使得该对的第一个顶点在第一集中,第二个顶点在第二集中。因此,树的每对顶点将被精确地处理一次(在这些顶点的lca中)
https://codeforces.com/contest/1499/problem/F
CF 1499 F
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll = long long;
using ULL = unsigned long long;
using PII = pair<ll, ll>;
using PLI = pair<ll, int>;
const int maxn = 6010;
const int maxm = 1e6 + 10;
const ll INF = 1e18;
const int mod = 998244353;
const double eps = 1e-8;
int n, m, k, q;
vector<int> g[maxn];
int f[maxn][maxn], tmp[maxn], dep[maxn], mxd[maxn];
void dfs(int u, int fa) {
dep[u] = mxd[u] = dep[fa] + 1;
f[u][0] = 1;
for (auto v : g[u]) {
if (v == fa) continue;
dfs(v, u);
int n = mxd[u] - dep[u], m = mxd[v] - dep[u];
for (int i = 0; i < maxn; i++) tmp[i] = 0;
for (int i = 0; i <= n; i++) {
for (int j = 0; j <= m; j++) {
tmp[i] = (tmp[i] + 1ll * f[u][i] * f[v][j] % mod) % mod;
if (i + j + 1 <= k) tmp[max(i, j + 1)] = (tmp[max(i, j + 1)] + 1ll * f[u][i] * f[v][j] % mod) % mod;
}
}
for (int i = 0; i <= max(n, m); i++) f[u][i] = tmp[i];
mxd[u] = max(mxd[u], mxd[v]);
}
}
void WTCh() {
cin >> n >> k;
for (int i = 1; i < n; i++) {
int a, b;
cin >> a >> b;
g[a].push_back(b);
g[b].push_back(a);
}
dfs(1, 0);
ll res = 0;
for (int i = 0; i <= k; i++) res = (res + f[1][i]) % mod;
cout << res << endl;
}
int main() {
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int w_ = 1;
//cin >> w_;
while (w_--) {
WTCh();
}
return 0;
}
字符串
AC自动机
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int n, m;
const int maxn = 1e5 + 10;
int a[maxn], root[maxn], idx;
vector<int> nums;
int find(int x) {
return lower_bound(nums.begin(), nums.end(), x) - nums.begin();
}
struct Node {
int l, r;
int cnt;
}tr[maxn * 4 + maxn * 17];
int build(int l, int r) {
int p = idx++;
if (l == r) {
return p;
}
int mid = l + r >> 1;
tr[p].l = build(l, mid);
tr[p].r = build(mid + 1, r);
return p;
}
int insert(int p, int l, int r, int x) {
int q = idx++;
tr[q] = tr[p];
if (l == r) {
tr[q].cnt++;
return q;
}
int mid = l + r >> 1;
if (x <= mid) tr[q].l = insert(tr[p].l, l, mid, x);
else tr[q].r = insert(tr[p].r, mid + 1, r, x);
tr[q].cnt = tr[tr[q].l].cnt + tr[tr[q].r].cnt;
return q;
}
int query(int q, int p, int l, int r, int k) {
if (l == r) return l;
int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
int mid = l + r >> 1;
if (k <= cnt) return query(tr[q].l, tr[p].l, l, mid, k);
else return query(tr[q].r, tr[p].r, mid + 1, r, k - cnt);
}
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
nums.push_back(a[i]);
}
sort(nums.begin(), nums.end());
nums.erase(unique(nums.begin(), nums.end()), nums.end());
root[0] = build(0, nums.size() - 1);
for (int i = 1; i <= n; i++) {
root[i] = insert(root[i - 1], 0, nums.size() - 1, find(a[i]));
}
while (m--) {
int l, r, k;
cin >> l >> r >> k;
cout << nums[query(root[r], root[l - 1], 0, nums.size() - 1, k)];
}
return 0;
}
后缀数组
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 1e6 + 10;
int n, m;
char s[maxn];
int sa[maxn], x[maxn], y[maxn], c[maxn], rk[maxn], height[maxn];
void get_sa() {
for (int i = 1; i <= n; i++) c[x[i] = s[i]]++;
for (int i = 2; i <= m; i++) c[i] += c[i - 1];
for (int i = n; i >= 1; i--) sa[c[x[i]]--] = i;
for (int k = 1; k <= n; k <<= 1) {
int num = 0;
for (int i = n - k + 1; i <= n; i++) y[++num] = i;
for (int i = 1; i <= n; i++) {
if (sa[i] > k) y[++num] = sa[i] - k;
}
for (int i = 1; i <= m; i++) c[i] = 0;
for (int i = 1; i <= n; i++) c[x[i]]++;
for (int i = 2; i <= m; i++) c[i] += c[i - 1];
for (int i = n; i >= 1; i--) sa[c[x[y[i]]]--] = y[i], y[i] = 0;
swap(x, y);
x[sa[1]] = 1, num = 1;
for (int i = 2; i <= n; i++) {
x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;
}
if (num == n) break;
m = num;
}
}
void get_height() {
for (int i = 1; i <= n; i++) rk[sa[i]] = i;
for (int i = 1, k = 0; i <= n; i++) {
if (rk[i] == 1) continue;
if (k) k--;
int j = sa[rk[i] - 1];
while (i + k <= n && j + k <= n && s[i + k] == s[j + k]) k++;
height[rk[i]] = k;
}
}
int main() {
cin >> s + 1;
n = strlen(s + 1), m = 122;
get_sa();
get_height();
for (int i = 1; i <= n; i++) cout << sa[i] << " ";
cout << endl;
for (int i = 1; i <= n; i++) cout << height[i] << " ";
cout << endl;
return 0;
}
后缀自动机
一、SAM的性质:
SAM是个状态机。一个起点,若干终点。原串的所有子串和从SAM起点开始的所有路径一一对应,不重不漏。所以终点就是包含后缀的点。
每个点包含若干子串,每个子串都一一对应一条从起点到该点的路径。且这些子串一定是里面最长子串的连续后缀。
SAM问题中经常考虑两种边:
(1) 普通边,类似于Trie。表示在某个状态所表示的所有子串的后面添加一个字符。
(2) Link、Father。表示将某个状态所表示的最短子串的首字母删除。这类边构成一棵树。
二、SAM的构造思路
endpos(s):子串s所有出现的位置(尾字母下标)集合。SAM中的每个状态都一一对应一个endpos的等价类。
endpos的性质:
(1) 令 s1,s2 为 S 的两个子串 ,不妨设 |s1|≤|s2| (我们用 |s| 表示 s 的长度 ,此处等价于 s1 不长于 s2 )。则 s1 是 s2 的后缀当且仅当 endpos(s1)⊇endpos(s2) ,s1 不是 s2 的后缀当且仅当 endpos(s1)∩endpos(s2)=∅ 。
(2) 两个不同子串的endpos,要么有包含关系,要么没有交集。
(3) 两个子串的endpos相同,那么短串为长串的后缀。
(4) 对于一个状态 st ,以及任意的 longest(st) 的后缀 s ,如果 s 的长度满足:|shortest(st)|≤|s|≤|longsest(st)| ,那么 s∈substrings(st) 。
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
using ll = long long;
const int maxn = 2e6 + 10;
int tot = 1, last = 1;
struct Node {
int len, fa;
int ch[26];
}node[maxn];
char str[maxn];
ll f[maxn], res;
int h[maxn], e[maxn], ne[maxn], idx;
void extend(int c) {
int p = last, np = last = ++tot;
f[tot] = 1;
node[np].len = node[p].len + 1;
for (; p && !node[p].ch[c]; p = node[p].fa) node[p].ch[c] = np;
if (!p) node[np].fa = 1;
else {
int q = node[p].ch[c];
if (node[q].len == node[q].len + 1) node[np].fa = q;
else {
int nq = ++tot;
node[nq] = node[q], node[nq].len = node[p].len + 1;
node[q].fa = node[np].fa = nq;
for (; p && node[p].ch[c] == q; p = node[p].fa) node[p].ch[c] = nq;
}
}
}
void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
void dfs(int u) {
for (int i = h[u]; ~i; i = ne[i]) {
dfs(e[i]);
f[u] += f[e[i]];
}
if (f[u] > 1) res = max(res, f[u] * node[u].len);
}
int main() {
cin >> str;
for (int i = 0; str[i]; i++) extend(str[i] - 'a');
memset(h, -1, sizeof(h));
for (int i = 2; i <= tot; i++) add(node[i].fa, i);
dfs(1);
cout << res << endl;
return 0;
}
杂
四维问题
#include <iostream>
#include <cstring>
#include <iomanip>
#include <functional>
#include <cmath>
#include <bitset>
#include <algorithm>
#include <vector>
#include <queue>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
using namespace std;
#define IOS \
ios::sync_with_stdio(false); \
cin.tie(0), cout.tie(0)
#define endl '\n'
using ll = long long;
using PII = pair<int, int>;
using PLI = pair<ll, int>;
const int maxn = 1e6 + 10;
const int maxm = 2;
const ll INF = 1e18;
const int mod = 1e9 + 7;
int n, m, k, q;
ll a[maxn], last[maxn];
ll res[maxn];
struct Segment_Tree {
void build(int u, int l, int r) {
tr[u].l = l, tr[u].r = r;
if (l == r) {
tr[u].sum = a[l];
// tr[u].maxv = a[l];
return;
}
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
//tr[u].maxv = max(tr[u << 1].maxv, tr[u << 1 | 1].maxv);
}
void update(int u, ll val) {
tr[u].sum += (tr[u].r - tr[u].l + 1) * val;
// tr[u].maxv += val;
tr[u].lazy += val;
}
void pushdown(int u) {
if (tr[u].lazy) {
update(u << 1, tr[u].lazy), update(u << 1 | 1, tr[u].lazy);
tr[u].lazy = 0;
}
}
void modify(int u, int l, int r, ll val) {
if (tr[u].l >= l && tr[u].r <= r) {
update(u, val);
return;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if (l <= mid) modify(u << 1, l, r, val);
if (r > mid) modify(u << 1 | 1, l, r, val);
pushup(u);
}
ll query(int u, int l, int r) {
if (tr[u].l >= l && tr[u].r <= r) {
return tr[u].sum;
}
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
ll res = 0;
if (l <= mid) res += query(u << 1, l, r);
if (r > mid) res += query(u << 1 | 1, l, r);
return res;
}
struct Node {
int l, r;
ll sum;
ll lazy;
}tr[maxn * 4];
}t1, t2;
struct Query {
ll l, r, k, id;
};
vector<Query> b[maxn];
void add(int i, int l, int r, int x) {
t1.modify(1, l, r, (ll)x * (n - i + 1));
t2.modify(1, l, r, x);
}
ll query(int i, int l, int r) {
return t1.query(1, l, r) - t2.query(1, l, r) * (n - i);
}
void Jared_McDs() {
cin >> n >> q;
t1.build(1, 1, n);
t2.build(1, 1, n);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
for (int i = 1; i <= q; i++) {
int l, r, x, y;
cin >> l >> r >> x >> y;
b[x - 1].push_back({l, r, -1, i});
b[y].push_back({l, r, 1, i});
}
set<ll> st;
st.insert(0);
for (int i = 1; i <= n; i++) {
add(i, i, i, i);
st.insert(i);
if (last[a[i]]) {
auto pre = prev(st.find(last[a[i]])), nxt = next(st.find(last[a[i]]));
ll l = *pre, x = last[a[i]], r = *nxt;
add(i, l + 1, x, r - x);
st.erase(last[a[i]]);
}
last[a[i]] = i;
for (auto [l, r, k, id] : b[i]) {
if (k < 0) res[id] -= query(i, l, r);
else res[id] += query(i, l, r);
}
}
for (int i = 1; i <= q; i++) cout << res[i] << endl;
}
int main() {
#ifdef LOCAL
freopen("in.in", "r", stdin);
freopen("out.out", "w", stdout);
#endif
IOS;
int w_ = 1;
// cin >> w_;
while (w_--) {
Jared_McDs();
}
return 0;
}
高精度
using i128 = __int128;
const i128 inf = 1e36;
istream &operator>>(istream &is, i128 &n) {
n = 0;
string s;
is >> s;
for (auto c : s) {
n = 10 * n + c - '0';
}
return is;
}
ostream &operator<<(ostream &os, i128 n) {
if (!n) return os << 0;
string s;
while (n) {
s += '0' + n % 10;
n /= 10;
}
reverse(s.begin(), s.end());
return os << s;
}
ModInt
template<const int T>
struct ModInt {
const static int mod = T;
int x;
ModInt(int x = 0) : x(x % mod) {}
ModInt(long long x) : x(int(x % mod)) {}
int val() { return x; }
ModInt operator + (const ModInt &a) const { int x0 = x + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }
ModInt operator - (const ModInt &a) const { int x0 = x - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }
ModInt operator * (const ModInt &a) const { return ModInt(1LL * x * a.x % mod); }
ModInt operator / (const ModInt &a) const { return *this * a.inv(); }
bool operator == (const ModInt &a) const { return x == a.x; };
bool operator != (const ModInt &a) const { return x != a.x; };
void operator += (const ModInt &a) { x += a.x; if (x >= mod) x -= mod; }
void operator -= (const ModInt &a) { x -= a.x; if (x < 0) x += mod; }
void operator *= (const ModInt &a) { x = 1LL * x * a.x % mod; }
void operator /= (const ModInt &a) { *this = *this / a; }
friend ModInt operator + (int y, const ModInt &a){ int x0 = y + a.x; return ModInt(x0 < mod ? x0 : x0 - mod); }
friend ModInt operator - (int y, const ModInt &a){ int x0 = y - a.x; return ModInt(x0 < 0 ? x0 + mod : x0); }
friend ModInt operator * (int y, const ModInt &a){ return ModInt(1LL * y * a.x % mod);}
friend ModInt operator / (int y, const ModInt &a){ return ModInt(y) / a;}
friend ostream &operator<<(ostream &os, const ModInt &a) { return os << a.x;}
friend istream &operator>>(istream &is, ModInt &t){return is >> t.x;}
ModInt pow(int64_t n) const {
ModInt res(1), mul(x);
while(n){
if (n & 1) res *= mul;
mul *= mul;
n >>= 1;
}
return res;
}
ModInt inv() const {
int a = x, b = mod, u = 1, v = 0;
while (b) {
int t = a / b;
a -= t * b; swap(a, b);
u -= t * v; swap(u, v);
}
if (u < 0) u += mod;
return u;
}
};
using mint = ModInt<1000000007>;
mint inv[maxn]; // 某个数逆元
void init(){
inv[1] = 1;
for(int i = 2; i < maxn; i++)
inv[i] = mint(mod - mod / i) * inv[mod % i];
}
随机数
mt19937_64 rnd(random_device{}());
uniform_int_distribution<ll> dist(0, LLONG_MAX);
for (int i = 1; i <= m; i++) {
int l, r;
cin >> l >> r;
ll h = dist(rnd);
d[l] += h;
if (r + 1 <= n) d[r + 1] -= h;
}
子集前缀和
和前面是一样的,只不过按集合元素拆分维度。
有一个集合 N N N,元素个数为n个,对其中任意一个子集 U U U,赋予一个值 A U A_U AU, 现在要求 N N N的所有子集 S S S各自的前缀和,即求 B S B_S BS满足
B S = ∑ U ⊂ S A U B_{S} = \sum_{U \subset S} A_U BS=U⊂S∑AU
如果直接对每个子集,枚举其子集求解,复杂度可达 O ( ∑ i = 0 n ( n i ) 2 i ) = O ( ( 1 + 2 ) n ) = O ( 3 n ) \mathcal{O}(\sum_{i = 0}^{n} \binom{n}{i} 2^i ) =\mathcal{O}((1+2)^n) = \mathcal{O}(3^n) O(∑i=0n(in)2i)=O((1+2)n)=O(3n)
我们可以按元素拆分为n维,每个维度的值有两个,0和1。用下标表示法可以表示法就是i和j只有0,1两种取值。
按照集合表示就是,设 s ∈ S s\in S s∈S为 S S S的一个元素有
B S = ∑ U ⊂ S A U = ∑ s ∈ V ∧ V ⊂ S A V + ∑ s ∉ V ∧ V ⊂ S A V B_S = \sum_{U\subset S} A_U = \sum_{s \in V \wedge V\subset S } A_{V } + \sum_{s \notin V \wedge V\subset S } A_{V } BS=U⊂S∑AU=s∈V∧V⊂S∑AV+s∈/V∧V⊂S∑AV
同理,我们可以得到如下代码
for (int i = 0; i < n; ++i) {
for (int j = 1; j < 1 << n; ++j) {
if (j & (1 << i)) {
a[j] += a[j ^ (1 << i)];
}
}
}