启智树提高组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;
}