数据结构杂题

简介

本博客主要用于记录一些神鸡数据结构题,并记录一些有趣的 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;
} 
posted @ 2026-07-20 14:14  Hty111  阅读(3)  评论(0)    收藏  举报