天天看点

[vijos1070]新年趣事之游戏(次小生成树)

传送门

我用的最原始的办法:

先求最小生成树然后枚举删除一条最小生成树上的边

每次跑kruskal

然后时间就是 O(nm+mlogm)

有人会问为什么没有那个log

因为每次只要打个删除标记,就可以避免重新排序,就可以降下来复杂度了

还有更高效的做法,一会儿再补,现在要去上课了(滑稽)

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define ll long long
#define R register
using namespace std;
inline int read(){
    int x=;char ch=' ';int f=;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')f=-,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*+ch-'0',ch=getchar();
    return x*f;
}
const int N=;
struct edge{
    int x,y,w;
    inline bool operator < (const edge& b) const {
        return w<b.w;
    }
}e[N*N];
int n,m,del,ans=;
int id[N],fa[N],tot,cnt;
inline int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void solve(){
    for(int i=;i<=n;i++)fa[i]=i;
    int num=;cnt=n;
    for(int i=;i<=m;i++){
        if(del==i)continue;
        int u=find(e[i].x);int v=find(e[i].y);
        if(u==v)continue;
        fa[u]=v;
        num+=e[i].w;
        cnt--;
        if(cnt==)break;
    }
    if(cnt!=)return;
    ans=min(ans,num);
}
int main(){
    n=read();m=read();
    for(int i=;i<=n;i++)fa[i]=i;
    for(int i=;i<=m;i++){
        e[i].x=read();e[i].y=read();e[i].w=read();
    }
    sort(e+,e+m+);
    int num=;cnt=n;
    for(int i=;i<=m;i++){
        int u=find(e[i].x);int v=find(e[i].y);
        if(u==v)continue;
        fa[u]=v;
        num+=e[i].w;
        id[++tot]=i;
        cnt--;
        if(cnt==)break;
    }
    if(cnt!=){
        printf("Cost: -1\nCost: -1");
        return ;
    }
    printf("Cost: %d\n",num);
    for(int i=;i<=tot;i++){
        del=id[i];
        solve();
    }
    if(ans==){
        printf("Cost: -1");
    }
    else{
        printf("Cost: %d",ans);
    }
    return ;
}