[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) 函数, 表示将 LR 这两个 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;
}
posted @ 2020-09-24 16:16  JiaZP  阅读(179)  评论(0编辑  收藏  举报