题解:P11265 【模板】静态区间半群查询
来点 u 选科技!
原文:并查集线段树。
尝试改造一下 zkw 线段树,我们知道 zkw 线段树的区间查询就是
得益于堆式存储,我们有几条可以利用的性质:首先,一次查询
离线后,对 lca 从深到浅做扫描线,可以使用带权并查集维护每个叶子到当前根的半群信息,更新时暴力从儿子往父亲连边即可,并且这个并查集是可以带路径压缩的。
那我缺的按秩合并这块谁给我补啊?实际上你可以发现,因为我们总是把儿子连到父亲上,所以如果我们直接钦定一个点的秩为它的高度,则连边的过程和按秩合并是几乎一样的,于是时间复杂度分析也可以套用。由此我们得到了一个很好写的
不知道为啥跑的不快,难过了。
using info = pair<mat, mat>;
info mul(const info& x, const info& y) { return {mul(x.first, y.first), mul(y.second, x.second)}; }
constexpr int kN = 1e6 + 2, kL = 21;
int n, m, q, ans, f[kN * 4];
mat a[kN];
info e[kN * 4], vf[kN * 4];
vector<pair<int, int>> rq[kL];
int F(int x) {
if (!f[x]) {
return x;
}
vf[x] = mul(vf[x], vf[exchange(f[x], F(f[x]))]);
return f[x];
}
signed main() {
cin.tie(nullptr)->sync_with_stdio(false);
cin >> n >> q, rnd.init(), out.init();
for (int i = 1; i <= n; ++i) rnd.genmat(a[i]);
m = (1 << __lg(n + 1) + 1);
auto _e = [](int i) -> mat& { return (i & 1) ? e[i ^ 1].first : e[i ^ 1].second; };
for (int i = 1; i <= n; ++i) {
_e(m + i) = a[i];
}
for (int i = m - 1; i >= 1; --i) {
_e(i) = mul(e[i * 2 + 1].second, e[i * 2].first);
}
for (int i = 1, l, r; i <= q; ++i) {
rnd.genqry(l, r, n);
l += m - 1, r += m + 1;
rq[__lg(l ^ r)].emplace_back(l, r);
}
for (int i = __lg(m); i; --i) {
for (auto [l, r] : rq[__lg(m) - i]) {
F(l), F(r);
out.setres(mul(vf[l].first, vf[r].second));
}
for (int j = (1 << i); j < (1 << i + 1); ++j) {
f[j] = j / 2, vf[j] = e[j];
}
}
return cout << out.ans << endl, 0;
}