bzoj1977 次小生成树

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 。

 
首先应该想到的暴力是:枚举每一条树边,然后再枚举非树边。但是这样的时间复杂度是m^2只能过50%
后来可以考虑,先进行一遍最小生成树,然后对于每一条非树边,找到这个非树边起点、终点在树上位置,然后求出这两个点最短路径上的最大边,再以这条非树边进行替换。
如图,红边是非树边,我们需要找到u,v两点的树上路径的最长边,然后替换为红边
 
但是!如果这条最长边是等于那条非树边的,就不能替换。故此我们需要记录次长边并且保证次长边严格小于最长边。
那么问题来了:怎么记录最长、次长边?怎么找到两个点之间的最短距离?
显而易见,我们需要用倍增LCA来解决.没有学过LCA的同学可以先去写一下模板再做这道题。
我们记录tree[i][j]代表i点向上2^j的点得编号
maxx[i][j]表示i向上2^j的最大值,maxn[i][j]表示次大值。
根据LCA基本算法,我们能得出:tree[i][j]=tree[tree[i][j-1][j-1],其中maxn、maxx的具体维护看代码,主要是不重不漏(我开始漏了一种情况,调了一下午)
预处理出tree等数组,跑LCA时,记录ma为这条最短路上的最长路径,边跑变更新,但一定记住要时刻保证ma不等于更新的那条非树边!
最后ans=sum(最小生成树和)-ma+val[i](非树边边权)
代码如下:
#include <iostream>
#include <cstdio> 
#include <algorithm>
#include <cstring>
#include <queue>
#define REP(i,k,n)  for(int i=k;i<=n;i++)
#define in(a) a=read()
#define MAXN 100010
using namespace std;
inline int read(){
    int x=0,f=1;
    char ch=getchar();
    for(;!isdigit(ch);ch=getchar())
        if(ch=='-')
            f=-1;
    for(;isdigit(ch);ch=getchar())
        x=x*10+ch-'0';
    return x*f;
}
queue <int> Q;
long long sum=0,ans=(1ll<<50); 
int n,m;
int cnt,book[MAXN<<2],f[MAXN];
int vis[MAXN],depth[MAXN],tree[MAXN][30],maxx[MAXN][30],maxn[MAXN][30];
int total=0,head[MAXN],to[MAXN<<2],nxt[MAXN<<2],val[MAXN<<2];
struct node{
    int x,y,z;
}l[MAXN<<2];
bool cmp(node a,node b){
    return a.z<b.z;
}
inline int getf(int k){
    if(f[k]==k)  return k;
    return f[k]=getf(f[k]);
}
inline void adl(int a,int b,int c){
    total++;
    to[total]=b;
    val[total]=c;
    nxt[total]=head[a];
    head[a]=total;
    return ;
}
inline void BFS(){//预处理
    Q.push(1);
    depth[1]=1;
    vis[1]=1;
    while(!Q.empty()){
        int u=Q.front();
        Q.pop();
        REP(j,1,21){
            tree[u][j]=tree[tree[u][j-1]][j-1];
            if(maxx[u][j-1]>maxx[tree[u][j-1]][j-1]){
                maxx[u][j]=maxx[u][j-1];
                maxn[u][j]=max(maxx[tree[u][j-1]][j-1],maxn[u][j-1]);
            }
            if(maxx[u][j-1]<maxx[tree[u][j-1]][j-1]){
                maxx[u][j]=maxx[tree[u][j-1]][j-1];
                maxn[u][j]=max(maxx[u][j-1],maxn[tree[u][j-1]][j-1]);
            }
            if(maxx[u][j-1]==maxx[tree[u][j-1]][j-1]){
                maxx[u][j]=maxx[u][j-1];
                maxn[u][j]=max(maxn[u][j-1],maxn[tree[u][j-1]][j-1]);
            }
        }
        for(int e=head[u];e;e=nxt[e])
            if(!vis[to[e]]){
                vis[to[e]]=1;
                depth[to[e]]=depth[u]+1;
                tree[to[e]][0]=u;
                maxx[to[e]][0]=val[e];
                Q.push(to[e]);
            }
    }
    return ;
}
inline int lca(int u,int v,int c){//lca
    if(depth[u]<depth[v])  swap(u,v);
    int d=depth[u]-depth[v];
    int ma=-999999999;
    for(int i=0;(1<<i)<=d;i++)//提到同一高度
        if((1<<i)&d){
            if(maxx[u][i]==c)  ma=max(ma,maxn[u][i]);
            else  ma=max(ma,maxx[u][i]);
            u=tree[u][i];
        //    cout<<u<<" "<<ma<<endl;
        }
    if(u==v){
        if(ma==-999999999)  return 0;
        return ma;
    }
    for(int i=21;i>=0;i--)//两点开跑
        if(tree[u][i]!=tree[v][i]){
            if(maxx[u][i]==c && maxx[v][i]==c)  
                ma=max(ma,max(maxn[u][i],maxn[v][i]));
            if(maxx[u][i]==c && maxx[v][i]!=c)
                ma=max(ma,max(maxn[u][i],maxx[v][i]));
            if(maxx[v][i]==c && maxx[u][i]!=c)
                ma=max(ma,max(maxn[v][i],maxx[u][i]));
            if(maxx[u][i]!=c && maxx[v][i]!=c)
                ma=max(ma,max(maxx[v][i],maxx[u][i]));
            u=tree[u][i];
            v=tree[v][i];
        }//最后lca是他们的父亲,所以要再更新一次
    if(maxx[u][0]==c && maxx[v][0]==c)  
        ma=max(ma,max(maxn[u][0],maxn[v][0]));
    if(maxx[u][0]==c && maxx[v][0]!=c)
        ma=max(ma,max(maxn[u][0],maxx[v][0]));
    if(maxx[v][0]==c && maxx[u][0]!=c)
        ma=max(ma,max(maxn[v][0],maxx[u][0]));
    if(maxx[u][0]!=c && maxx[v][0]!=c)
        ma=max(ma,max(maxx[v][0],maxx[u][0]));
    return ma;
}
int main(){
    in(n);in(m);
    REP(i,1,n)  f[i]=i;
    REP(i,1,m){
        in(l[i].x);
        in(l[i].y);
        in(l[i].z);
    }
    sort(l+1,l+m+1,cmp);
    REP(i,1,m){
        int f1=getf(l[i].x),f2=getf(l[i].y);
        if(f1!=f2){
            cnt++;
            book[i]=1;
            f[f2]=f1;
            sum+=(long long)l[i].z;
            adl(l[i].x,l[i].y,l[i].z);
            adl(l[i].y,l[i].x,l[i].z);
        }
        if(cnt==n-1)  break;
    }
    BFS();
    REP(i,1,m)
        if(!book[i]){//枚举所有的非树边
        //    cout<<l[i].x<<" "<<l[i].y<<" "<<l[i].z<<endl;
        //    cout<<lca(l[i].x,l[i].y,l[i].z)<<endl;
            ans=min(ans,sum-(long long)lca(l[i].x,l[i].y,l[i].z)+(long long)l[i].z);
        }
    cout<<ans;
}
        

 

 
posted @ 2018-09-28 20:37  Dijkstra·Liu  阅读(353)  评论(1编辑  收藏  举报