• [SDOI2019]世界地图


    题目链接

    显然我们可以用 LCT 维护 MST 的方法搞出前缀和后缀的最小生成树,但是这种方法并不能支持前缀拼后缀,因为复杂度大概是 (O(nmq log)) 的。

    考虑每次都是左边 MST 的最右边一列与右边 MST 的最左边一列之间对应连边,我们只需要知道那两列中的点在 MST 中两两之间的链的最大值。换句话说,我们只需要知道两个 MST 将那两列作为关键点的虚树的链上最大值。于是我们可以每个 MST 只维护其最左最右的两列为关键点的虚树,边权为原树上的链的最大值,这样点数边数就都是 (O(n)) 的了。每次合并就直接暴力 Kruskal 即可。复杂度:(O(mnlog n + qnlog 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
    ", merge(suf[r + 1], pre[l - 1], rgt[m]).get_ans());
    	}
    	return 0;
    }
    
  • 相关阅读:
    Ubuntu安装adobe的Source Code Pro
    Oracle实现主键自增的几种方式
    Oracle主键自增
    activity 根据流程实例ID删除流程实例、删除流程部署
    解决报错:错误1130- Host xxx is not allowed to connect to this MariaDb server
    Idea-每次修改JS文件都需要重启Idea才能生效解决方法
    html中<a href> </a>的用法
    Mysql8_数据库基础操作
    java反射及Method的Invoke方法(转载)
    oracle查询数据插入时间
  • 原文地址:https://www.cnblogs.com/JiaZP/p/13724654.html
Copyright © 2020-2023  润新知