1977: [BeiJing2010组队]次小生成树 Tree

11 篇文章 0 订阅
10 篇文章 0 订阅

1977: [BeiJing2010组队]次小生成树 Tree

Time Limit: 10 Sec   Memory Limit: 512 MB
Submit: 3002   Solved: 752
[ Submit][ Status][ Discuss]

Description

小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值)  这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

Input

第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

Output

包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)

Sample Input

5 6
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6

Sample Output

11

HINT

数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

Source

[ Submit][ Status][ Discuss]

先构建出一棵最小生成树,按照选择的边集,构建出这棵树
考虑所有未选择的边,如果加入其中一条,一定形成一个环,将环上权值最大的边替换,就得到次小生成树
但是,如果权值与当前边相同,则并不难直接替换,因此,同时维护下严格次大值,这样就解决了
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
 
const int maxn = 1E5 + 10;
const int maxm = 3E5 + 30;
const int T = 14;
const int INF = ~0U>>1;
typedef long long LL;
 
struct E{
    int x,y,w; E(){}
    E(int x,int y,int w): x(x),y(y),w(w){}
    bool operator < (const E &B) const {return w < B.w;}
}edgs[maxm];
 
struct E2{
    int to,w; E2(){}
    E2(int to,int w): to(to),w(w){}
};
 
int n,m,Fr,Sc,delta = INF,fa[maxn][T],F[maxn],L[maxn],fr[maxn][T],sc[maxn][T];
bool bo[maxm]; LL sum;
 
vector <E2> v[maxn];
 
int getF(int k) {return k == F[k] ? k : F[k] = getF(F[k]);}
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret*10 + ch - '0',ch = getchar();
    return ret;
}
 
void Merge(int &nf,int &ns,const int &mf,const int &ms)
{
    if (nf == mf) ns = max(ns,ms);
    else if (nf > mf) ns = max(ns,mf);
    else ns = max(nf,ms),nf = mf;
}
 
void Dfs(int x,int from)
{
    for (int i = 1; i < T; i++)
    {
        int y = fa[x][i-1]; fa[x][i] = fa[y][i-1];
        Merge(fr[x][i],sc[x][i],fr[x][i-1],sc[x][i-1]);
        Merge(fr[x][i],sc[x][i],fr[y][i-1],sc[y][i-1]);
    }
    for (int i = 0; i < v[x].size(); i++)
    {
        E2 e = v[x][i];
        if (e.to == from) continue;
        fa[e.to][0] = x; fr[e.to][0] = e.w;
        sc[e.to][0] = -INF; L[e.to] = L[x] + 1; Dfs(e.to,x);
    }
}
 
void LCA(int p,int q)
{
    if (L[p] < L[q]) swap(p,q);
    for (int j = T - 1; j >= 0; j--)
        if (L[p] - (1<<j) >= L[q])
            Merge(Fr,Sc,fr[p][j],sc[p][j]),p = fa[p][j];
    if (p == q) return;
    for (int j = T - 1; j >= 0; j--)
        if (fa[p][j] != fa[q][j])
        {
            Merge(Fr,Sc,fr[p][j],sc[p][j]); p = fa[p][j];
            Merge(Fr,Sc,fr[q][j],sc[q][j]); q = fa[q][j];
        }
    Merge(Fr,Sc,fr[p][0],sc[p][0]); Merge(Fr,Sc,fr[q][0],sc[q][0]);
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint(); m = getint();
    for (int i = 1; i <= m; i++)
    {
        int x = getint(),y,w;
        y = getint(); w = getint();
        edgs[i] = E(x,y,w);
    }
    for (int i = 1; i <= n; i++) F[i] = i;
    sort(edgs + 1,edgs + m + 1);
    for (int i = 1; i <= m; i++)
    {
        int fx = getF(edgs[i].x),fy = getF(edgs[i].y);
        if (fx == fy) continue;
        bo[i] = 1; F[fx] = fy; sum += 1LL * edgs[i].w;
        v[edgs[i].x].push_back(E2(edgs[i].y,edgs[i].w));
        v[edgs[i].y].push_back(E2(edgs[i].x,edgs[i].w));
    }
    L[n / 2] = 1; Dfs(n / 2,0);
     
    for (int i = 1; i <= m; i++)
    {
        if (bo[i]) continue;
        Fr = Sc = -INF; LCA(edgs[i].x,edgs[i].y);
        if (Fr < edgs[i].w) delta = min(delta,edgs[i].w - Fr);
        else if (Sc != -INF) delta = min(delta,edgs[i].w - Sc);
    }
    cout << 1LL * delta + sum << endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值