首页 > 其他分享 >古人云:时间线段树 爽!时间线段树学习笔记。

古人云:时间线段树 爽!时间线段树学习笔记。

时间:2024-02-26 15:34:56浏览次数:20  
标签:sz int 线段 笔记 古人云 MAXN tp void

嘛,这个东西虽然叫时间线段树,但是和线段树好像关系并不大,只是借用了一下线段树的结构。

算法介绍

这个算法是用来解决这类问题的:每个操作只在一段时间内生效,然后询问某个时间点所有操作的贡献。

于是我们考虑离线,对整个时间序列建一个线段树,每次操作相当于是在这个线段树上进行了区间修改,所以我们可以利用线段树的结构将他的生效时间分摊到 \(O(\log n)\) 个时间区间上。

然后这个东西一般要搭配一些可撤销的数据结构,典中典的就是可撤销并查集。

我们先把所有修改加到这个线段树里,然后整体查询一次,查询的时候,假设我们当前的时间区间是 \([l,r]\),我们就先把当前节点维护的所有操作加到这个可撤销的数据结构里,然后在根节点(或者在某个节点)统计答案,离开这个节点的时候把操作撤销掉。

所以最终插入和查询的复杂度就都是 \(O(n\log n)\)。

当然如果你有可持久化的数据结构也可以直接用可持久化的数据结构,但有些数据结构不太好可持久化,或者可持久化之后要多一个 \(\log\),这个时候就可以用时间线段树。

例题 1:LG5787/BZOJ4025 二分图

题意

给定若干条边,每条边有一个生效区间 \([s,e]\),问在每个时间刻这个图是不是二分图。

题解

并查集判是否为二分图,然后上时间线段树。

怎么并查集判二分图:对于每条边 \(u,v\),将 \(u,v+n\) 与 \(v,u+n\) 合并,如果发现一条边 \(u,v\) 在同一个集合中则出现了奇环。

const int MAXN = 2e5 + 5;
int n, m, T, fa[MAXN], sz[MAXN], ans[MAXN];

struct _stck {
	pair<int, int> a[MAXN];
	int __tp;
	void pop() { --__tp; }
	void push(pair<int, int> x) { a[++__tp] = x; }
	int size() { return __tp; }
	auto top() { return a[__tp]; }
} st;

struct _node {
	vector<pair<int, int>> q;
	void addEdge(int u, int v) {
		q.push_back(make_pair(u, v));
	}
} tr[MAXN << 2];

int find(int x) {
	while (fa[x] != x) x = fa[x];
	return x;
}

void merge(int x, int y) {
	if (x == y) return;
	if (sz[x] < sz[y]) swap(x, y);
	fa[y] = x;
	st.push(make_pair(y, sz[x]));
	sz[x] += sz[y];
}

void modify(int p, int l, int r, int L, int R, int u, int v) {
	if (L <= l && r <= R) return tr[p].addEdge(u, v);
	if (L <= mid) modify(lson, l, mid, L, R, u, v);
	if (mid < R) modify(rson, mid + 1, r, L, R, u, v);
}

void undo() {
	auto x = st.top().first, y = st.top().second;
	sz[fa[x]] = y;
	fa[x] = x;
	st.pop();
}

void query(int p, int l, int r) {
	const int top = st.size();
	// add this node's edges
	for (auto [u, v]:tr[p].q) {
		int fu = find(u), fv = find(v);
		if (fu != fv) merge(find(u + n), fv), merge(find(v + n), fu);
		else {
			for (int i = l; i <= r; ++i) ans[i] = 1;
			while ((int)st.size() > top) undo();
			return;
		}
	}
	if (l != r) {
		query(lson, l, mid);
		query(rson, mid + 1, r);
	}
	while ((int)st.size() > top) undo();
}

void work() {
	cin >> n >> m >> T;
	for (int i = 1; i <= 2 * n; ++i) fa[i] = i, sz[i] = 1;
	for (int i = 1, s, e, u, v; i <= m; ++i) {	
		cin >> u >> v >> s >> e;
		modify(1, 1, T, s + 1, e, u, v);
	}
	query(1, 1, T);
	for (int i = 1; i <= T; ++i) cout << (ans[i] ? "No" : "Yes") << endl;
}

例题 2:CF1423H Virus

题意

有 \(n\) 个人,给定 \(k\),一开始在第一天,有 \(q\) 次操作,每次操作形如:

  1. 告诉你 \(x,y\) 在今天见面了。
  2. 查询 \(x\) 在过去 \(k\) 天与多少人见面(或间接见面)了(包括自己)。
  3. 告诉你今天过完了,下一天。

\(n,k\le 10^5,q\le 5\times 10^5\)。

题解

设当前在第 \(t\) 天,考虑操作 \(1\),其实影响了 \([t,t+k)\) 这个区间,于是转换为对 \([t,t+k)\) 这个区间的区间修改就做完了。

const int MAXN = 5e5 + 5;
int n, q, k, fa[MAXN], sz[MAXN];

struct _stck {
	pair<int, int> a[MAXN];
	int __tp;
	void pop() { --__tp; }
	void push(pair<int, int> x) { a[++__tp] = x; }
	int size() { return __tp; }
	auto top() { return a[__tp]; }
} st;

int find(int x) {
	while (x != fa[x]) x = fa[x];
	return x;
}

void merge(int x, int y) {
	x = find(x), y = find(y);
	if (x == y) return;
	if (sz[x] < sz[y]) swap(x, y);
	fa[y] = x;
	sz[x] += sz[y];
	st.push(make_pair(x, y));
}

void undo() {
	int x = st.top().first, y = st.top().second;
	fa[y] = y;
	sz[x] -= sz[y];
	st.pop();
}

vector<pair<int, int>> tr[MAXN << 2];
vector<pair<int, int>> qr[MAXN];
int ans[MAXN];

void modify(int p, int l, int r, int L, int R, int x, int y) {
	if (L <= l && r <= R) {
		tr[p].push_back({x, y});
		return;
	}
	if (L <= mid) modify(lson, l, mid, L, R, x, y);
	if (mid < R) modify(rson, mid + 1, r, L, R, x, y);
}

void query(int p, int l, int r) {
	const int top = st.size();
	for (auto [x, y]:tr[p]) merge(x, y);
	if (l == r) {
		for (auto [x, id]:qr[l]) {
			ans[id] = sz[find(x)];
		}
	}
	else query(lson, l, mid), query(rson, mid + 1, r);
	while (st.size() > top) undo();
}

struct _query {
	int op, x, y, t;
} qs[MAXN];

void work() {
	cin >> n >> q >> k;
	for (int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1;
	int T = 1, t = 0;
	for (int i = 1; i <= q; ++i) {
		int op, x, y;
		cin >> op;
		if (op == 3) ++T;
		if (op == 1) {
			cin >> x >> y;
			qs[++t] = {op, x, y, T};
		}
		if (op == 2) {
			cin >> x;
			qs[++t] = {op, x, i, T};
		}
	}
	qs[t + 1].t = INT_MAX;
	auto getTime = [&](int p) {
		int l = 0, r = t + 1;
		while (l + 1 < r) {
			if (qs[mid].t <= p) l = mid;
			else r = mid;
		}
		return l;
	};
	for (int i = 1; i <= t; ++i) {
		if (qs[i].op == 1) {
			const int endTime = getTime(qs[i].t + k - 1);
			modify(1, 1, q, i, endTime, qs[i].x, qs[i].y);
		}
		if (qs[i].op == 2) {
			qr[i].push_back({qs[i].x, qs[i].y});
		}
	}
	query(1, 1, q);
	for (int i = 1; i <= q; ++i)
		if (ans[i]) cout << ans[i] << endl;
}

标签:sz,int,线段,笔记,古人云,MAXN,tp,void
From: https://www.cnblogs.com/XiaoQuQu/p/18034431

相关文章

  • 容斥原理学习笔记
    前言可能需要一点二项式定理和二项式反演的相关知识。有许多不足还请指出。公式经典容斥\(A_1,A_2,\cdots,A_n\)均为有限集,\(A_i\subseteqS\),则\[\left\vert\bigcup\limits_{i=1}^nA_i\right\vert=\sum\limits_{k=1}^n(-1)^{k-1}\sum\limits_{1\lei_1<i_2<\cdots<i_k\le......
  • MAUI Blazor+MASA开发安卓应用学习笔记 - 设置图标和初始屏幕
    上一期已经成功生成了APK能成功安装到手机上了,图标和初始屏幕很难看,接下来着手修改图标和初始屏幕一、修改图标打开项目文件.csproj,找到以下代码<!--AppIcon--><MauiIconInclude="Resources\AppIcon\appicon.svg"ForegroundFile="Resources\AppIcon\appiconfg.svg"Colo......
  • Vue 学习笔记15--用户代码片段
    { //Placeyour全局snippetshere.Eachsnippetisdefinedunderasnippetnameandhasascope,prefix,bodyand //description.Addcommaseparatedidsofthelanguageswherethesnippetisapplicableinthescopefield.Ifscope //isleftemptyor......
  • MAUI Blazor+MASA开发安卓应用学习笔记 - 设置APP格式、名称、版本信息
    上一期说到了如何生成APP应用,生成的文件是AAB格式的,这个格式安装不是很方便,如果能生成APK就好了 一、设置APP格式打开项目文件.csproj,在PropertyGroup下添加属性<AndroidPackageFormat>apk</AndroidPackageFormat>二、设置名称和版本信息在项目文件里,可以设置全局的应用......
  • MAUI Blazor+MASA开发安卓应用学习笔记 - 新建项目和发布
    PS:开个新坑,内容都是全新接触的东西,包括MAUI,Blazor,MASA等等。整个项目都边学习边做的,有什么错的地方望大神指教。 学习开发安卓应用,我们的最终目标就是要生成一个APP应用,并能成功的在手机端打开。那么,我们首先要解决的就是怎么生成APP应用。一、创建一个.NETMAUIBlazor应用(注......
  • 万字Java进阶笔记总结
    JavaApi字符串String注意:Java中“==”操作符的作用:基本数据类型:比较的是内容。引用数据类型比较的是对象的内存地址。StringBuffer/StringBuilder由于String是字符串是常量,它们的值在创建之后不能更改。如果我们使用这个String频繁进行操作,会有性能问题,这个时候就需要......
  • 《系统科学方法概论》第五章读书笔记
    首先,组建现代管理系统必须遵循远离平衡态原则。这也就是说,构成管理系统的人员必须具有各不相同的能力和水平,尤其是作为管理系统的第一把手应具备把握全局的能力和权力,而其他成员则不必具备把握全局的能力和权力,或只需具备把握某-方面全局的能力和权力即可。如果-一个管理系统的所......
  • 《系统科学方法概论》第二章读书笔记
    系统工程不是无缘无故地提出的,而是为了解决目前实践或科研中所遇到的问题而搞起来的。所以,在设计或研制一项系统工程前,应先把所遇到的问题搞清楚。问题是什么?就是现实状况和主观需要之间的矛盾。在阐述问题时,要注意三个方面:-是注意问题的过去、现在和将来的发展趋势,也就是要考......
  • 《系统科学方法概论》第三章读书笔记
    信息论最初是作为一种通信理论而被建立起来的,它也主要用于通信领域。但是经过30多年的发展,到了20世纪70年代,信息论已不仅仅是一种通信理论,而是越过了通信领域,广泛渗人其他学科了。例如在物理学研究中,一些科学家就把信息与熵1]联系起来,以说明系统的有序和无序的相互转化。在生物学......
  • 《系统科学方法概论》第一章读书笔记
    系统思想的发展史即人们对物质世界系统性认识的历史。这个历史经历了古代、近代、现代三个发展时期。任何系统都处于--定的环境中,并与环境发生着物质、能量或信息交换关系,脱离一定环境的系统是不存在的。什么是环境?所谓环境是指系统整体存在和发展的全部条件的总和。环境是构成......