• 题解——[[SHOI2010]最小生成树]


    [SHOI2010]最小生成树

    • 前言 真心佩服mwy当场想出网络流算法,蒋神树上解决问题,看来ssw02建图和抽象还是太菜了 -7.13 (天气之子7.19日本上映)

    题目大意:
    在一个合法的连通图中,每条边都有一个边权,我们钦定一条边。然后对图进行一些操作:将某一条边的权值+1或-1。请问至少多少次后可以使钦定的边出现在该图的最小生成树中。
    Luogu传送门:[SHOI2010]最小生成树
    解题思路:
    说实话:我们一定要跳出题目的局限性,毕竟网络流是属于图算法。
    我们考虑,为什么我们钦定的边会不在最小生成树上,很简单,有更多比它更小的边。并且,能够更新我们钦定边的边一定会和我们钦定的边成环(我们考虑kruscal的实现过程,如果想不明白,可以看看 严格次小生成树 )。
    在这道题中更为简单,由上述条件,我们只需要对于每一条比我们钦定边target小的边建立一条权值为
    权值 = t[ target ].val - t[ i ].val + 1的流边即可。然后转化为最小割模型即可。
    模型转化:
    我很想把它提出来讲。
    我们既然要找到一条符合的边,那么就是要让最小生成树经过钦定边target,经过的前提就是可以到达target边上两点的其他路径都没有target更优,那么,然这些边增大即可。这样一想,感觉就是让这两点之间的其他路径都比target边的边权大。
    我们对这些原本更小的边都计算于target边权的差值,然后跑最小割。
    口糊合法性:
    1.这些边都小于target的边权,如果更大,那么它本身就不可以能优于target边,就不可能在target之前进入最小生成树
    2.由1可知,上述建出的流图中无负边权(流量为负),所以流算法是可行的。(最小割 = 最大流 )
    3.如果target就在原本的最小生成树上,那么流图是不连通的,所以出题人是卡不了的。

    注:楼主作死写了HLPP,完全可以用Dinic打,而且LLL优化后还更快一些,当然随机数据HLPP绝对有时间上的优势。
    AC code:

    #include<bits/stdc++.h>
    using namespace std;
    const  int  MAXN = 100010 , inf = ( 1 << 30 ) ;
    inline int read()
    {
        int s = 0,w = 1;
        char g = getchar();
        while(g<'0'||g>'9'){if(g=='-')w*=-1;g = getchar();}
        while(g>='0'&&g<='9'){s = (s<<3)+(s<<1)+g-'0';g = getchar();}
        return s*w;
    }
    int  tot = 1 , to[ MAXN ] , nex[ MAXN ] , head[ 10005 ] , w[ MAXN ] , N , M , T , s , target ;
    int  d[ 10005 ] , e[ 10005 ] , h[ 10005 ] , numh[ MAXN ] ;
    bool v[ MAXN ] ;
    
    struct ap{
        int from , too , val ;
    }t[ MAXN ];
    
    void  add( int  x , int  y , int  z ){
        tot++; to[ tot ] = y , w[ tot ] = z , nex[ tot ] = head[ x ] , head[ x ] = tot;
        tot++; to[ tot ] = x , w[ tot ] = 0 , nex[ tot ] = head[ y ] , head[ y ] = tot;
    }
    void  relabel( int  u ){
        h[ u ] = inf ;
        for( register int i = head[ u ] ; i ; i = nex[ i ] ){
            if( w[ i ] && h[ to[ i ] ] < h[ u ] )
                h[ u ] = h[ to[ i ] ] + 1 ;
        }
    
    }
    struct cmp{
        inline bool operator () (int a,int b) const{
            return h[a]<h[b];
        }
    };//重载d
    priority_queue< int , vector<int> , cmp > q ;
    queue< int >Bq;
    void bfs(){
        for( register int i = 1 ; i <= N ; i ++ )h[ i ] = 1210 ;
        h[ T ] = 0 ;
        while( !Bq.empty() )Bq.pop();
        Bq.push( T ); v[ T ] = true ;
        while( !Bq.empty() ){
            int u = Bq.front();Bq.pop();
            v[ u ] = false ;
            for( register int i = head[ u ] ; i ; i = nex[ i ] ){
                if( w[ i^1 ] && h[ to[ i ] ] > h[ u ] ){
                    h[ to[ i ] ] = h[ u ] + 1 ;
                    if( !v[ to[ i ] ] ){
                        Bq.push( to[ i ] );
                        v[ to[ i ] ] = true ;
                    }
                }
            }
        }
        return;
    }
    void push_(int u){
        for( register int i = head[ u ] ; i ; i = nex[ i ]){
            if( w[ i ] && h[ to[ i ] ] == h[ u ] - 1 ){ 
                int  k = min( w[ i ] , e[ u ] ) ;
                w[ i ] -= k , w[ i^1 ] += k ;
                e[ u ] -= k , e[ to[ i ] ] += k ;
                if( (!v[ to[ i ] ]) && to[ i ] != T && to[ i ] != s ){
                    q.push( to[ i ] ) ;
                    v[ to[ i ] ] = true ;
                }
                if( !e[u] )break;
            }
        }
    }
    int HLPP(){//作死使用HLPP 
        bfs();
        if( h[ s ] == ( 1 << 30 ) )return 0;
        h[ s ] = N ;
        for( register int i = 1 ; i <= N ; i++ )
            if( h[ i ] < 20000 )numh[ h[ i ] ]++ ;
        for( register int i = head[ s ] ; i ; i = nex[ i ] ){
            if( w[ i ] ){
                e[ s ] -= w[ i ] , e[ to[ i ] ] += w[ i ] ;
                w[ i^1 ] += w[ i ] ,  w[ i ] = 0;
                if( to[ i ] != T && ( !v[ to[ i ] ]) && to[ i ] != s ){
                    q.push( to[ i ] ) ; v[ to[ i ] ] = true ;
                }
            }
        }
        while(!q.empty()){
            int  u = q.top() ;v[ u ] = false ;
            q.pop() ;push_( u ) ;
            if( e[ u ] ){
                numh[ h[ u ] ]-- ;
                if( numh[ h[ u ] ] == 0 ){
                    for( register int i = 1 ; i <= N ; i++ ){
                        if( i != s && i != T && h[ i ] > h[ u ] && h[ i ] <= N  )
                            h[ i ] = N + 1 ;
                    }
                }
                relabel( u ) ;
                numh[ h[ u ] ]++ ;
                q.push( u ) ;
                v[ u ] = true ;
            }
        }
        return e[ T ] ;
    } 
    void  prepare(){
        //freopen( "construct.in" , "r" , stdin ) ;
        //freopen( "construct.out" , "w" , stdout) ; 
    }
    int main(){
        
        N = read() ; M = read() ; target = read();
        for( register int i = 1 ; i <= M ; i++ )t[ i ].from=read(),t[ i ].too=read(),t[i].val=read();
        s = t[ target ].from ; T = t[ target ].too ;
        for( register int i = 1 ; i <= M ; i++ ){
            if( t[ i ].val <= t[target].val && i !=target ){
                add( t[ i ].from , t[ i ].too , t[ target ].val - t[ i ].val + 1 ) ;
                add( t[ i ].too , t[ i ].from , t[ target ].val - t[ i ].val + 1 ) ;        
            }
        }
        cout<<HLPP();
        return 0;
    }
    

    非常感谢您耐心读完了这篇博客,作者水平有限,本文难免有错误和叙述不精准,还请给为大佬指出,谢谢。

  • 相关阅读:
    Unity shader with lightmap
    清理数据库日志
    Sqlserver数据库还原一直显示“正在还原…”解决方法
    如何查看 SQL Server 执行的历史 SQL 语句记录?
    MYSQL 命令行大全 (简洁、明了、全面)
    C#Datatable导入sqlserver数据库中,三种常见,快捷的方法
    MongoDB下载页面
    等级保护和分级保护区别与联系
    等级保护和分级保护
    SQL2005EXPress自动备份
  • 原文地址:https://www.cnblogs.com/ssw02/p/11179543.html
Copyright © 2020-2023  润新知