题解——[[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;
}

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

原文地址:https://www.cnblogs.com/ssw02/p/11179543.html