[SDOI2019]世界地图
显然我们可以用 LCT 维护 MST 的方法搞出前缀和后缀的最小生成树,但是这种方法并不能支持前缀拼后缀,因为复杂度大概是 \(O(nmq \log)\) 的。
考虑每次都是左边 MST 的最右边一列与右边 MST 的最左边一列之间对应连边,我们只需要知道那两列中的点在 MST 中两两之间的链的最大值。换句话说,我们只需要知道两个 MST 将那两列作为关键点的虚树的链上最大值。于是我们可以每个 MST 只维护其最左最右的两列为关键点的虚树,边权为原树上的链的最大值,这样点数边数就都是 \(O(n)\) 的了。每次合并就直接暴力 Kruskal 即可。复杂度:\(O(mn\log n + qn\log n)\)
然而实现起来还是有不少难度的。一种较为简单的方法是,直接开一个结构体表示最小生成树,存 \(tot\)(点数) \(sum\)(除了虚树上的边以外的 MST 的边权和) 以及 \(E\)(虚树的边),然后写一个 merge(L, R, w)
函数, 表示将 L
和 R
这两个 MST 中间连边权为 w
的边,返回拼合的 MST。保证每个 MST 的前 \(n\) 个点是最左边的点,后 \(n\) 个点是最右边的点。每次建虚树可以通过两边 DFS \(O(n)\) 实现。具体见代码。
struct MST {
int tot;
ll sum;//sum of real edges - sum of Max Edges
vector<Edge> E;
MST() { tot = sum = 0; E.clear(); }
MST(int *w) {
tot = n; sum = 0;//Bug4
E.clear();
for (int i = 1; i < n; ++i) E.push_back((Edge){i, i + 1, w[i]});//Bug4
}
inline ll get_ans() {
ll res = sum;
for (unsigned int i = 0; i < E.size(); ++i) res += E[i].w;
return res;
}
}pre[N], suf[N];
int tag[N];
int fath[N];
int find(int cur) {
return fath[cur] == cur ? cur : fath[cur] = find(fath[cur]);
}
/*----------------*/
/*两遍 DFS 建虚树*/
bool dfs_imp(int cur, int faa) {
int tot = 0;
for (int i = head[cur]; i; i = e[i].nxt) {
int to = e[i].to; if (to == faa) continue;
tot += dfs_imp(to, cur);
}
tag[cur] |= (tot >= 2);
return tot + tag[cur] > 0;
}
void dfs_link(int cur, int faa, int lst, int mxval) {
if (tag[cur]) {
if (lst) E.push_back((Edge){tag[cur], lst, mxval}), nwans -= mxval;
lst = tag[cur];//bug1
mxval = 0;
}
for (int i = head[cur]; i; i = e[i].nxt) {
int to = e[i].to; if (to == faa) continue;
dfs_link(to, cur, lst, max(mxval, e[i].val));
}
}
/*两边 DFS 建虚树*/
/*---------------------*/
inline MST merge(MST a, MST b, int *w) {
int tot = a.tot + b.tot;
E.clear();
for (int i = 1; i <= n; ++i) E.push_back((Edge){a.tot - n + i, a.tot + i, w[i]});
for (unsigned int i = 0; i < a.E.size(); ++i) E.push_back(a.E[i]);
for (unsigned int i = 0; i < b.E.size(); ++i)
E.push_back((Edge){b.E[i].u + a.tot, b.E[i].v + a.tot, b.E[i].w});//bug2
nwans = a.sum + b.sum;
sort(E.begin(), E.end());
for (int i = 1; i <= tot; ++i) fath[i] = i, tag[i] = false, head[i] = 0;
ecnt = 0;
for (unsigned int i = 0; i < E.size(); ++i) {
int u = E[i].u, v = E[i].v, w = E[i].w;
int fu = find(u), fv = find(v);
if (fu == fv) continue;
fath[fu] = fv;//bug3
addedge(fu, fv, w), addedge(fv, fu, w);
nwans += w;
}
E.clear();
for (int i = 1; i <= n; ++i) tag[i] = true;
for (int i = tot - n + 1; i <= tot; ++i) tag[i] = true;
dfs_imp(1, 0);
int ptot = 0;
for (int i = 1; i <= tot; ++i) if (tag[i]) tag[i] = ++ptot;
dfs_link(1, 0, 0, 0);
MST mst; mst.tot = ptot; mst.sum = nwans; mst.E = E;
return mst;
}
int main() {
Read_Part::gen();
pre[1] = MST(dn[1]);
for (int i = 2; i <= m; ++i)
pre[i] = merge(pre[i - 1], MST(dn[i]), rgt[i - 1]);
suf[m] = MST(dn[m]);
for (int i = m - 1; i; --i)
suf[i] = merge(MST(dn[i]), suf[i + 1], rgt[i]);
int q; read(q);
while (q--) {
int l, r; read(l), read(r);
printf("%lld\n", merge(suf[r + 1], pre[l - 1], rgt[m]).get_ans());
}
return 0;
}