数据结构

线性结构

单调队列

滑动窗口优化 DP

例题 LOJ 10180. 「一本通 5.5 练习 1」烽火传递

给定一个数轴,上面有 n 个点,选中每个点有一定代价,现要求连续的 m 个点中至少选一个,求最小代价。

/**
*
* 考虑 dp
* 设 dp[i] 表示选择第 i 个来保证 [1, i] 合法的最小花费
* 转移方程:
* dp[i] = cost[i] + min{dp[j]} (i - m <= j < i)
* 后面这个 min 可以滑动窗口优化
*
*/

const int MAXN = 2e5 + 10;

int q1[MAXN], q2[MAXN];
int h1 = 1, t1 = 1, h2 = 1, t2 = 1; // 左闭右开

int n, m;
int ss[MAXN];
int dp[MAXN];

int main() {
n = read(); m = read();
for (int i = 1; i <= n; ++i) ss[i] = read();
for (int i = 1; i <= n; ++i) {
// 队尾弹出不优的,维护单调性
while (h1 != t1 && q1[t1 - 1] >= dp[i - 1]) --t1, --t2;
// 队尾入队
q1[t1++] = dp[i - 1]; q2[t2++] = i - 1;
// 队头弹出过期的
while (h2 != t2 && q2[h2] < i - m) ++h1, ++h2;
// 更新答案
dp[i] = ss[i] + q1[h1];
}
int ans = 0x7f7f7f7f;
for (int i = n - m + 1; i <= n; ++i) ans = std::min(ans, dp[i]);
printf("%d\n", ans);
return 0;
}


ST 表

const int MAXN = (100000 + 10) * 2;
const int MAXLOG = 17 + 10; // floor(log2(100000 + 10))

int n, q;
int Table[MAXN][MAXLOG];

void BuildTable() {
for (int j = 1; (1 << j) <= n; ++j) {
for (int i = 1; i + (1 << j) - 1 <= n; ++i) {
Table[i][j] = std::max(Table[i][j-1], Table[i + (1 << (j - 1))][j - 1]);
}
}
}

int Query(int l, int r) {
int k = std::log(r - (l - 1)) / std::log(2);
return std::max(Table[l][k], Table[r - ((1 << k) - 1)][k]);
}

int main() {
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; ++i) scanf("%d", &Table[i][0]);
BuildTable();
for (int i = 1; i <= q; ++i) {
int l = 0, r = 0;
scanf("%d %d", &l, &r);
printf("%d\n", Query(l, r));
}
return 0;
}


分块

区间加法,单点查询

const int MAXN = 50000 + 10;

int n;
int ss[MAXN];

namespace Blocks {
int blk[MAXN], add[MAXN]; int siz;

int blkFirst(int id) { // id 属于的块开始位置的编号
return (blk[id] - 1) * siz + 1;
}
int blkLast(int id) { // id 属于的块结束位置的编号
return blk[id] * siz;
}
void init() {
siz = std::sqrt(n);
for (int i = 1; i <= n; ++i) blk[i] = (i - 1) / siz + 1;
}
void Add(int l, int r, int k) {
for (int i = l; i <= std::min(blkLast(l), r); ++i) {
ss[i] += k;
}
if (blk[l] != blk[r]) {
for (int i = blkFirst(r); i <= r; ++i) {
ss[i] += k;
}
}
for (int i = blk[l] + 1; i <= blk[r] - 1; ++i) add[i] += k;
}
int Query(int r) {
return add[blk[r]] + ss[r];
}
}


树图结构

并查集

struct DSU {
int seq[MAXN];

int Find(int x) { return !seq[x] ? x : seq[x] = Find(seq[x]); }
void Merge(int x, int y) {
x = Find(x); y = Find(y);
if (x != y) seq[x] = y;
}
} dsu;


线段树

struct SegtTree {
int sum[MAXN << 2]; int tag[MAXN << 2];

#define ls (p << 1)
#define rs (p << 1 | 1)

void Update(int p) { sum[p] = sum[ls] + sum[rs]; }
void buildTree(int p, int l, int r, int *k) {
if (l == r) { sum[p] = k[l]; return; }
int mid = (l + r) >> 1;
buildTree(ls, l, mid, k); buildTree(rs, mid + 1, r, k);
Update(p);
}
void Add(int p, int l, int r, int k) {
(sum[p] += 1ll * k * (r - l + 1) % ha) %= ha;
(tag[p] += k) %= ha;
}
void Pushdown(int p, int l, int r) {
if (tag[p]) {
int mid = (l + r) >> 1;
Add(ls, l, mid, tag[p]); Add(rs, mid + 1, r, tag[p]);
tag[p] = 0;
}
}
void Modify(int p, int l, int r, int ll, int rr, int k) {
if (l == ll && rr == r) { Add(p, l, r, k); return; }
Pushdown(p, l, r);
int mid = (l + r) >> 1;
if (rr <= mid) Modify(ls, l, mid, ll, rr, k);
else if (mid + 1 <= ll) Modify(rs, mid + 1, r, ll, rr, k);
else { Modify(ls, l, mid, ll, mid, k); Modify(rs, mid + 1, r, mid + 1, rr, k); }
Update(p);
}
int Query(int p, int l, int r, int ll, int rr) {
if (l == ll && rr == r) return sum[p] % ha;
Pushdown(p, l, r);
int mid = (l + r) >> 1;
if (rr <= mid) return Query(ls, l, mid, ll, rr) % ha;
else if (mid + 1 <= ll) return Query(rs, mid + 1, r, ll, rr) % ha;
else return (Query(ls, l, mid, ll, mid) + Query(rs, mid + 1, r, mid + 1, rr)) % ha;
}
} segt;


树状数组

区间加法

int n, tree[MAX_SIZE];
// n 为元素个数,tree[] 为树状数组维护的前缀和

int lowbit(int x) { return (x) & (-x); }

void Modify(int pos, int x) {
// 将 pos 位置的数加上 x
for (; pos <= n; pos += lowbit(pos)) tree[pos] += x;
}

int Query(int pos) {
// 查询 [1,pos] 之间的数的和
int ret = 0;
for (; pos >= 1; pos -= lowbit(pos)) ret += tree[pos];
return ret;
}

int rangeQuery(int l, int r) {
// 查询 [l,r] 之间的数的和
return Query(r) - Query(l - 1);
}


Trie 树

struct Trie {
static const int MAXNODE = 300000 + 10;

struct Node {
int cntson; bool end;
int next[27];
Node() { end = cntson = 0; memset(next, 0, sizeof next); }
} node[MAXNODE]; const int root = 1; int cnt = 1;

void Insert(const std::string &x) {
int len = (int) x.size(); int u = root;
for (int i = 0; i < len; ++i) {
int &nxt = node[u].next[x[i] - 'a'];
if (!nxt) nxt = ++cnt, ++node[u].cntson;
u = nxt;
} node[u].end = true;
}
} trie;


哈希算法

字符串哈希

struct Hash {
static const int b1 = 29;
static const int b2 = 131;
static const int m1 = 998244353;
static const int m2 = 1e9 + 7;

int h1, h2;
Hash() { h1 = h2 = 0; }
void append(int x) {
h1 = 1ll * h1 * b1 % m1 + x; h1 %= m1;
h2 = 1ll * h2 * b2 % m2 + x; h2 %= m2;
}
void erase_prefix(Hash x, int len) {
h1 -= 1ll * x.h1 * pb1[len - 1] % m1; h1 += m1; h1 %= m1;
h2 -= 1ll * x.h2 * pb2[len - 1] % m2; h2 += m2; h2 %= m2;
}
bool operator == (const Hash &th) const {
return h1 == th.h1 && h2 == th.h2;
}
};


手写哈希表

int _RND = 0;

srand(time(0));
_RND = std::abs(rand() * rand() % 998244353);

struct HashMap {
static const int SZ = 1926097;
struct data {
long long u;
int v, nex;
data(long long _u = 0, int _v = 0, int _nex = 0) {
u = _u; v = _v; nex = _nex;
}
};
data e[SZ << 1];
int h[SZ], cnt;
int hash(long long u) { return u % SZ; }
bool locate(long long u) {
int hu = hash(u ^ _RND);
for (int i = h[hu]; i; i = e[i].nex) {
if (e[i].u == u) return true;
}
return false;
}
int& operator[](long long u) {;
int hu = hash(u ^ _RND);
for (int i = h[hu]; i; i = e[i].nex)
if (e[i].u == u) return e[i].v;
return e[++cnt] = (data) {u, 0, h[hu]}, h[hu] = cnt, e[cnt].v;
}
HashMap() {
cnt = 0;
memset(h, 0, sizeof(h));
}
} mp;