【BZOJ1977】【BeiJing2010】组队次小生成树Tree(生成树)

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 。

题目大意:求严格次小生成树。
题解:在普通次小生成树的基础上,当环上最大权值等于(u,v)的边权时是不能替换的,要用严格次大的边替换。所以在更新最大边权的同时,要记录一下次大边。

代码如下:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<math.h>
#define ll long long
#define inf 0x7f7f7f7f
#define N 100005
#define M 300005 
using namespace std;
ll read()
{
    ll x=0,f=1;
    char ch=getchar();
    while(ch>'9' || ch<'0') {if(ch=='-') f=-1;ch=getchar();}
    while(ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,fa[N],anse[N][20],m1[N][20],m2[N][20],dep[N],delta=0x7f7f7f7f;
ll ans;
struct edge
{
    int u,v,w;
    edge(){}
    edge(int a,int b,int c):u(a),v(b),w(c){}
    inline bool operator<(const edge& rhs) const {return w<rhs.w;}
}e[M];
bool in[M];
int to[M],nex[M],val[M],hd[N],tot;
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void add(int u,int v,int w)
{
    to[++tot]=v,nex[tot]=hd[u],val[tot]=w,hd[u]=tot;
    to[++tot]=u,nex[tot]=hd[v],val[tot]=w,hd[v]=tot;
}
void kruskal()
{
    sort(e+1,e+m+1);int cnt=0;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m && cnt<n-1;i++)
    {
        int u=e[i].u,v=e[i].v,w=e[i].w;
        if(find(u)==find(v)) continue;
        fa[find(u)]=find(v);
        cnt++;ans+=(ll)w;in[i]=1;
        add(u,v,w);
    }
}
void dfs(int u)
{
    for(int i=1;i<=18;i++)
    {
        anse[u][i]=anse[anse[u][i-1]][i-1];
        int t1=m1[u][i-1],t2=m1[anse[u][i-1]][i-1];
        m1[u][i]=max(t1,t2);
        m2[u][i]=max(m2[u][i-1],m2[anse[u][i-1]][i-1]);
        if(t1!=t2) m2[u][i]=max(m2[u][i],min(t1,t2));
    }
    for(int i=hd[u];i;i=nex[i])
    {
        if(to[i]==anse[u][0]) continue;
        anse[to[i]][0]=u;
        dep[to[i]]=dep[u]+1;
        m1[to[i]][0]=val[i];
        dfs(to[i]);
    }
}
int lca(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v);
    int t=dep[u]-dep[v];
    for(int i=18;i>=0;i--) 
    if(t&(1<<i)) u=anse[u][i];
    if(u==v) return u;
    for(int i=18;i>=0;i--)
    if(anse[u][i]!=anse[v][i]) u=anse[u][i],v=anse[v][i];
    return anse[u][0];
}
void query(int u,int v,int w)
{
    int max1=0,max2=0,h=dep[u]-dep[v];
    for(int i=18;i>=0;i--)
    if(h&(1<<i))
    {
        if(m1[u][i]>max1) max2=max1,max1=m1[u][i];  
        max2=max(max2,m2[u][i]);
    }
    if(w==max1) delta=min(delta,w-max2);
    else delta=min(delta,w-max1);
}
void solve(int x)
{
    int u=e[x].u,v=e[x].v,w=e[x].w,t=lca(u,v);
    query(u,t,w),query(v,t,w);
}
int main()
{
    n=read(),m=read();
    for(int i=1,x,y,z;i<=m;i++) x=read(),y=read(),z=read(),e[i]=edge(x,y,z);
    kruskal();dfs(1);
    for(int i=1;i<=m;i++) if(!in[i]) solve(i);
    printf("%lld\n",ans+(ll)delta);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值