LGP7840题解

给出一种新的理解方式,本质上和正解是一致的。

首先我们现在已经有了一个森林,我们现在要给他加一条边,加哪一条边是最优的呢?

假设加的边是 \((u,v)\),那么 \(((d[u]+1)^2-d[u]^2) \times val[u]+((d[v]+1)^2-d[v]^2) \times val[v]\) 一定是最小的,也就是两个点的 \((2 \times d[u]+1) \times val[u]\) 分别是整个集合中的最小值和次小值,且不连通。

于是我们对每个联通块分别开一个堆,再对所有联通块开一个大堆就能 \(O(n\log^2n)\) 做了。(需要合并堆,优先队列启发式合并的总复杂度是 \(O(n\log^2n)\) 的,当然不嫌麻烦也可以写一颗左偏树)

对刚才的模型做一些改变,假设我们连接了在同一个联通块中的两个点又会是怎么样的呢?

假设我们有四个点 \(a,b,c,d\),连接 \((a,b)(c,d)\)\((a,c)(b,d)\) 的权值是一样的,因为度数序列相同,接下来连接的两个点在对方的方案中都属于同一个联通块。

也就是说,两个点是否在同一个联通块中并不影响最终答案,因为我们是对其的度数进行了修改。

于是直接对所有点开一个大堆就好了,没必要分联通块什么的,复杂度 \(O(n\log n)\)

#include<cstdio>
#include<queue>
typedef unsigned ui;
struct Node{
	ui d,val;
	inline bool operator<(const Node&it)const{
		return (2*d+1)*val>(2*it.d+1)*it.val;
	}
}now;std::priority_queue<Node>q;unsigned long long ans;
signed main(){
	ui i,n;scanf("%u",&n);now.d=1;
	for(i=1;i<=n;++i)scanf("%u",&now.val),ans+=now.val,q.push(now);
	for(i=1;i<=n-2;++i){
		now=q.top();q.pop();
		ans+=(2*now.d+1)*now.val;
		if(++now.d!=n)q.push(now);
	}
	printf("%llu",ans);
}
posted @ 2022-01-11 14:32  Prean  阅读(26)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};