Joy OI【走廊泼水节】题解--最小生成树推论变式

  • 题目链接:

http://joyoi.org/problem/tyvj-1391

  • 思路:

首先这需要一个推论:

“给定一张无向图,若用\(k(k<n-1)\)条边构成一个生成森林(可以理解为多个互不相通的生成树),再从剩下的\(m-k\)条边中选出\(n-1-k\)条边构成改该图的最小生成树,则这\(m-k\)条边中一定包含连接两个不相连生成森林的最小边权的两点”

这个推论是由这个定理得到:

“一张无向图的最小生成树一定包含边权最小的那条边”,这个定理可以很容易地用反证法证得。

那么我们就可以开始了,若连接两个不连通的生成森林最小边权为\(e\),根据推论,想要让它变成一张完全图而最小生成树保持不变,当然是让剩下的点相连边的权值为\(e+1\)

那么让这两个生成森林变成完全图则需要\((e+1)*(size[A]*size[B]-1)\),\(size[K]\)为以K为父节点的生成森林所含的点数,减去1是因为两个之中已经有一条边权为\(e\)的边

根据贪心的思想,我们显然所有边从小到大排序,如果两顶点不在一个森林里,那么合并,加入贡献。

  • 代码(话说并查集路径压缩一开始写错了,查了好久的错,真是太蒻了):
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cctype>
#include <queue>
#include <vector>
#define ll long long 
#define ri register int
using namespace std;
const int maxn=6005;
const int inf=0xfffffff;
struct Edge{
    int f,t,val;
    bool operator <(const Edge &b)const{
        return val<b.val;
    }
}edge[maxn];
int n,t;
ll ans=0;
int size[maxn],fa[maxn];
template <class T>inline void read(T &x){
    x=0;int ne=0;char c;
    while(!isdigit(c=getchar()))ne=c=='-';
    x=c-48;
    while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    x=ne?-x:x;
    return ;
}
int get(int x){
    if(fa[x]!=x)fa[x]=get(fa[x]); 
    return fa[x];//注意路径压缩写法 
}
int main(){
    int u,v,d;
    read(t);
    while(t--){
        int ans=0;
        memset(edge,0,sizeof(edge));
        read(n);
        for(ri i=1;i<n;i++)
        read(edge[i].f),read(edge[i].t),read(edge[i].val);
        sort(edge+1,edge+n);
        for(ri i=1;i<=n;i++){fa[i]=i,size[i]=1;}
        for(ri i=1;i<n;i++){
            u=edge[i].f,v=edge[i].t,d=edge[i].val;
            //cout<<u<<' '<<v<<endl;
            u=get(u),v=get(v);
            //cout<<u<<' '<<v<<endl;
            if(u!=v){
                fa[u]=v;
                ans+=(d+1)*(size[u]*size[v]-1);
                size[v]+=size[u];
            }
        }
        printf("%d\n",ans);
    }   
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Rye-Catcher/p/9152811.html