启智树提高组Day2T2 涛涛的集合

支持四种操作:集合合并,集合加,(原序列上)区间清零,单点查询。其中单点查询有 \(10^7\) 次,其余 \(50000\) 次。\(n \le 2 \times 10^5\)

显然要用到并查集。但是怎么维护原序列上区间清零呢?这需要用到一个小技巧:维护每个点最后被清零的时间,查询就是当前的值减去最后被清零的值。这个可以用分块来维护。于是,单点查询 \(\times 2\),区间清零 \(=0\)

然后我们需要用一个能够快速 \(O(1)\) 查并查集的祖先节点,还要支持打集合加标记的并查集。这个可以启发式合并,集合 -> vector,合并的时候直接把小的那个集合的元素全部拿出来改掉信息,下放标记,扔到大的那个里面。注意为了适应大的集合,那些元素可能还要减去大的集合的加法标记。

总复杂度:\(O(q_0\sqrt n + nlogn + q_1)\)

inline void Pushdown(int cur) {
	if (tags[cur]) {
		for (register int i = st[cur]; i <= ed[cur]; ++i)
			val[i] = tags[cur];
		tags[cur] = 0;
	}
}
inline void modify(int l, int r, int v) {
	if (blong[l] == blong[r]) {
		Pushdown(blong[l]);
		for (register int i = l; i <= r; ++i)	val[i] = v;
		return ;
	}
	Pushdown(blong[l]); for (register int i = l; i <= ed[blong[l]]; ++i)	val[i] = v;
	Pushdown(blong[r]); for (register int i = r; i >= st[blong[r]]; --i)	val[i] = v;
	for (register int i = blong[l] + 1; i < blong[r]; ++i)
		tags[i] = v;
}
int opt[N], X[N], Y[N];
vector<pair<PII, int> > vt[N];
int main() {
	read(n), read(q);
	for (register int i = 1; i <= n; ++i)	anc[i] = i, vec[i].push_back(i);
	init();
	for (register int i = 1; i <= q; ++i) {
		read(opt[i]); read(X[i]), read(Y[i]);
		if (opt[i] == 3) {
			modify(X[i], Y[i], i);
		} else if (opt[i] == 4) {
			for (register int j = X[i]; j <= Y[i]; ++j) {
				int lst = tags[blong[j]] ? tags[blong[j]] : val[j];
				vt[i].push_back(MP(MP(i, j), 1));
				vt[lst].push_back(MP(MP(i, j), -1));
			}
		}
	}
	memset(val, 0, sizeof(val));
	for (register int i = 1; i <= q; ++i) {
		if (opt[i] == 1) {
			X[i] = anc[X[i]]; Y[i] = anc[Y[i]];
			if (X[i] != Y[i]) {
				if (vec[X[i]].size() > vec[Y[i]].size()) swap(X[i], Y[i]);
				for (register unsigned int j = 0; j < vec[X[i]].size(); ++j) {
					int cur = vec[X[i]][j];
					anc[cur] = Y[i];
					vec[Y[i]].push_back(cur);
					val[cur] += taga[X[i]] - taga[Y[i]];
				}
				vec[X[i]].clear();
			}
		} else if (opt[i] == 2) {
			X[i] = anc[X[i]];
			taga[X[i]] += Y[i];
		}
		for (register unsigned int j = 0; j < vt[i].size(); ++j) {
			int id = vt[i][j].first.first, type = vt[i][j].second;
			int cur = vt[i][j].first.second;
			ans[id] += type * (val[cur] + taga[anc[cur]]);
		}
	}
	for (register int i = 1; i <= q; ++i)
		if (opt[i] == 4)
			printf("%lld\n", ans[i]);
	return 0;
}
posted @ 2020-08-27 07:55  JiaZP  阅读(182)  评论(0编辑  收藏  举报