• 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;
    }
            
     
  • 相关阅读:
    在Eclipse中使用JUnit4进行单元测试(上)
    SVN和Subclipse安装和使用指南汇总
    在windows下搭建SVN服务器
    学会SVN的应用源代码托管
    SVN中检出(check out) 和 导出(export) 的区别
    .NET平台三层应用程序框架搭建(一)
    Winform dataGridview 为每一个单元格制定一个tooptip
    SQL row_number() over() 来自动产生行号
    Winform datagridview 设置单元格为只读属性
    SQL 把字符创分割成两个字符串
  • 原文地址:https://www.cnblogs.com/jason2003/p/9720630.html
Copyright © 2020-2023  润新知