NOIP 模拟赛 day11 T3 最小边权和 水题解
Description
给你一棵带边权的树,这棵树是某个完全图唯一的最小生成树。问原来的完全图中所有边可能的最小边权和是多少。完全图是任意两个点之间都有边相连的图。
\(\small T\leq 10\ \ \ N\leq 2\cdot 10^4\ \ \ w_i \leq 10^4\)
Solution
题目要求的是完全图的最小边权和,并且题目给了我们最小生成树,我们就要想怎么加,什么时候加其他的那些非树边。
如果我们想尽量早的加入尽可能小的非树边,我们就可以考虑,就按照最小生成树的加边进行遍历。
然后我们考虑,什么时候加哪些边。
一个很简单的例子:
这是一个完全图,照理来说,最小生成树应该是 \((1,2),(1,3)\) ,然后多出来的 \((2,3)\) 就能使最小生成树变成完全图。
所以说,当我们加边加到一定程度( \(N>=2\) ),完全图和最小生成树边数就不一样了,然后在新加了一条边的时候,我们就可以处理两者之间差的边,为了不对原树产生影响,这些边全部赋成目前加的树边的边权+1就行了。
注意新加的树边可能意味着两大块的合并,所以多出来的边的数量与所在集合大小有关,所以做模拟最小生成树的时候记录一下集合大小。
时间复杂度 \(O(NlogN)\) (代码清奇,绝对好懂)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e4+10;
ll t,n,fa[N],siz[N],ans;
struct mdzz{
ll u,v,w;
bool operator < (const mdzz& it) const{
return w<it.w;
}
}edge[N];
inline ll read(){
ll s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0'&&ch<='9') s=s*10+ch-'0',ch=getchar();
return s*w;
}
inline ll find(ll x){
if(fa[x]==x)return fa[x];
return fa[x]=find(fa[x]);
}
int main(){
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
t=read();
while(t--){
n=read();ans=0;
for(int i=1;i<=n;++i){
fa[i]=i;siz[i]=1;
}
for(int i=1;i<n;++i){
int u=read(),v=read(),w=read();
edge[i]=(mdzz){u,v,w};ans+=w;
}
sort(edge+1,edge+n);
for(int i=1;i<n;++i){
int uu=find(edge[i].u);
int vv=find(edge[i].v);
ans+=(siz[uu]*siz[vv]-1ll)*(edge[i].w+1ll);
fa[vv]=uu;siz[uu]+=siz[vv];siz[vv]=0;
}
printf("%lld\n",ans);
}
return 0;
}
刮大分!