数据结构杂题
简介
本博客主要用于记录一些神鸡数据结构题,并记录一些有趣的 trick。
“插入——标记——回收”算法解决函数复合问题
考虑这样一个函数复合问题:
给定一串函数 \(f_i\)(有可能是分段函数,即根据 \(x\) 的范围导致的 \(f\) 解析式不同),每次询问,给出 \(l, r, x\),求 \(f_l \circ f_{l + 1} \circ f_{l + 2} \circ \cdots \circ f_r \circ x\)。允许离线。
做法是:
- 将询问离线下来,考虑进行扫描线。
- 对于每一个询问,考虑将其两端点各拆成一个单点操作。
- 每个操作,就是将一个数 \(x\) 加入一个可重集(或删除出并获取答案)。
- 那么这样,就可以考虑如何全局维护一个集合的函数。
- 发现维护时有可能需要根据 \(x\) 的范围来确定函数种类/有无。
- 使用平衡树(FHQ Treap)来维护这件事情。
- 最终维护的总体时间复杂度为 \(\mathcal O(n \log{n})\)。
例题:QOJ #8672. 排队
题意:给定 \(f_i(x) = x + [x \in [l_i, r_i]]\),每次询问 \(f_l \circ f_{l + 1} \circ f_{l + 2} \circ \cdots \circ f_r \circ x\)。
将操作离线后,用“插入-标记-回收”算法处理,建一颗 FHQ Treap,然后每次对于一个函数,直接对平衡树内 \([l_i, r_i]\) 打上 \(+ 1\) 标签即可。
CODE
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
mt19937 gen(145);
inline int read(){
int x = 0; char ch = getchar();
while(ch <'0' || ch > '9') ch = getchar();
while(ch >= '0' && ch <= '9') x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
return x;
}
int n, q;
int l[N], r[N];
vector<int> in[N], out[N];
struct FHQ{
int val[N], rnd[N], ls[N], rs[N], tag[N], fa[N], stk[N];
int idx = 0, rt = 0, tot = 0;
pair<int, int> tmp = {0, 0};
int newnode(int v){ val[++ idx] = v, fa[idx] = 0, tag[idx] = 0, ls[idx] = rs[idx] = 0, rnd[idx] = gen(); return idx; }
void down(int u, int x){ val[u] += x, tag[u] += x; }
void down(int u){ if(tag[u]) down(ls[u], tag[u]), down(rs[u], tag[u]), tag[u] = 0; }
pair<int, int> split(int u, int x){
if(!u) return make_pair(0, 0);
down(u);
if(val[u] <= x) {
tmp = split(rs[u], x);
rs[u] = tmp.first;
fa[rs[u]] = u;
fa[tmp.second] = 0;
return {u, tmp.second};
} else {
tmp = split(ls[u], x);
ls[u] = tmp.second;
fa[ls[u]] = u;
fa[tmp.first] = 0;
return {tmp.first, u};
}
}
int merge(int u, int v){
if(!u || !v) return u + v;
if(rnd[u] > rnd[v]) return down(u), rs[u] = merge(rs[u], v), fa[rs[u]] = u, u;
else return down(v), ls[v] = merge(u, ls[v]), fa[ls[v]] = v, v;
}
int getfirst(int u){ while(ls[u]) down(u), u = ls[u]; return val[u]; }
int join(int x, int y){
int ans = 0;
while(x){
tmp = split(x, getfirst(y));
ans = merge(ans, tmp.first), x = tmp.second;
swap(x, y);
}
return merge(ans, y);
}
void load(int u){
while(fa[u]) u = fa[u], stk[++ tot] = u;
while(tot) down(stk[tot --]);
}
} T;
int to[N], ans[N];
int main(){
n = read(), q = read();
for(int i = 1; i <= n; i ++) l[i] = read(), r[i] = read();
for(int i = 1, x, y; i <= q; i ++) x = read(), y = read(), in[x].emplace_back(i), out[y].emplace_back(i);
for(int i = 1; i <= n; i ++){
for(auto j : in[i]){
to[j] = T.newnode(0);
T.rt = T.join(T.rt, to[j]);
}
int x, y, z;
tie(x, y) = T.split(T.rt, l[i] - 1), tie(y, z) = T.split(y, r[i]);
T.down(y, 1);
T.rt = T.merge(x, T.join(y, z));
for(auto j : out[i]){
T.load(to[j]), ans[j] = T.val[to[j]];
}
}
for(int i = 1; i <= q; i ++) printf("%d\n", ans[i]);
return 0;
}

浙公网安备 33010602011771号