[BZOJ2095]-[Poi2010]Bridges-二分答案+混合图欧拉回路判定

说在前面

写这道题顺便学了学混合图欧拉回路判定,感觉自己萌萌哒!
话说me网络流连反向边都忘记建了,居然还可以过样例???
一直以来都十分佩服样例数据,以及造数据的人,无论程序有什么bug都能跑对…


题目

BZOJ2095传送门

题面

(直接概括题目大意就没什么意思了hhhh)
YYD为了减肥,他来到了瘦海,这是一个巨大的海,海中有n个小岛,小岛之间有m座桥连接,两个小岛之间不会有两座桥,并且从一个小岛可以到另外任意一个小岛。现在YYD想骑单车从小岛1出发,骑过每一座桥,到达每一个小岛,然后回到小岛1。霸中同学为了让YYD减肥成功,召唤了大风,由于是海上,风变得十分大,经过每一座桥都有不可避免的风阻碍YYD,YYD十分ddt,于是用泡芙贿赂了你,希望你能帮他找出一条承受的最大风力最小的路线。

输入输出格式

输入格式:
第一行为两个用空格隔开的整数n(2<=n<=1000),m(1<=m<=2000),含义如题
接下来读入m行由空格隔开的4个整数a,b(1<=a,b<=n,a!=b),c,d(1<=c,d<=1000),表示第i座桥连接小岛a和b,从a到b承受的风力为c,从b到a承受的风力为d

输出格式:
如果无法完成减肥计划,则输出NIE,否则第一行输出最大承受风力的最小值


解法

题目就是要我们找一条欧拉回路(每个桥经过一次就好,不管方向),使得这条回路上权值最大的尽量小
二分答案是显然的,关键是如何check

每次二分一个mid,大于mid的边都不选,那么就有一些方向不能走了,原图就是一个混合图,问题就转化成了一个混合图判定欧拉回路问题(如果有一条边两个方向都不能走,那肯定不存在欧拉回路)
对于那些单向边,直接统计度数就可以。对于两个方向都可以走的边,先随便定一个方向,假设是u->v,统计度数,并且在网络图里加一条边u->v,流量为1。
最后遍历所有的点u,如果入度与出度之差为奇数,显然无解(某一次进去就出不来了,或者出来就进不去了)
如果u出度大于入度,那么加边S->u,流量(out-in)/2
如果u入度大于出度,那么加边u->T,流量(in-out)/2
最后满流才有欧拉回路

这样做的正确性:
双向边是随意定向的,因此这个定向可能是错的,才会导致一些点出度和入度不相等(如果欧拉回路本来存在的话)。如果把一条原本是v->u的边定成了u->v,那么u就多了出度,v就少了入度。要修正这个错误,就需要让u->v边反向,达到u出度-1,v入度+1的目的。也就是S->u->v->T这条流量的含义。当然反过来建也是可以的:S->v->u->T
如果最后不能满流,说明有一些点的入度没办法等于出度,那肯定不存在欧拉回路

成功get新姿势!


下面是自带大常数的代码

#include 
#include 
#include 
using namespace std ;

int N , M , ind[1005] , otd[1005] , head[1005] , tp , S = 0 , T = 1001 ;
struct Edges{
    int u , v , Uv , Vu ;
}e[2005] ;
struct Path{
    int pre , to , flow ;
}p[100005] ;

void In( int t1 , int t2 , int t3 ){
    p[++tp].pre = head[t1] ;
    p[ head[t1] = tp ].to = t2 ;
    p[tp].flow = t3 ;
}

int dis[1005] , que[1005] , fr , ba ;
bool Bfs(){
    fr = 1 , ba = 0 ;
    memset( dis , -1 , sizeof( dis ) ) ;
    dis[S] = 0 ; que[++ba] = S ;
    while( fr <= ba ){
        int u = que[fr++] ;
        for( int i = head[u] ; i ; i = p[i].pre ){
            int v = p[i].to ;
            if( !p[i].flow || dis[v] != -1 ) continue ;
            dis[v] = dis[u] + 1 ;
            que[++ba] = v ;
        }
    }
    return dis[T] != -1 ;
}

int dfs( int u , int flow ){
    if( u == T || !flow ) return flow ;
    int totf = 0 ;
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( dis[v] != dis[u] + 1 || !p[i].flow ) continue ;
        int nowf = dfs( v , min( flow , p[i].flow ) ) ;
        if( nowf ){
            flow -= nowf ;
            totf += nowf ;
            p[i].flow -= nowf ;
            p[i^1].flow += nowf ;
        }
    }
    if( !totf ) dis[u] = -1 ;
    return totf ;
}

bool check( int mid ){
    tp = 1 ; 
    memset( ind , 0 , sizeof( ind ) ) ;
    memset( otd , 0 , sizeof( otd ) ) ;
    memset( head , 0 , sizeof( head ) ) ;
    int ans = 0 , sum = 0 ;
    for( int i = 1 ; i <= M ; i ++ ){
        int u = e[i].u , v = e[i].v ;
        if( e[i].Uv <= mid && e[i].Vu <= mid ){
            In( u , v , 1 ) ;
            In( v , u , 0 ) ;
            otd[u] ++ , ind[v] ++ ;
        } else if( e[i].Uv <= mid )
            otd[u] ++ , ind[v] ++ ;
          else if( e[i].Vu <= mid )
            otd[v] ++ , ind[u] ++ ;
          else return false ;
    }
    for( int i = 1 ; i <= N ; i ++ ){
        int delta = otd[i] - ind[i] ;
        if( delta&1 ) return false ;
        if( delta > 0 ){
            In( S , i , delta / 2 ) ;
            In( i , S , 0 ) ;
            sum += delta/2 ;
        }
        if( delta < 0 ){
            In( i , T , -delta / 2 ) ;
            In( T , i , 0 ) ;
        }
    }
    while( Bfs() )
        ans += dfs( S , 0x3f3f3f3f ) ;
    return ans == sum ;
}

void solve(){
    int lf = 1 , rg = 1000 , ans = -1 ;
    while( lf <= rg ){
        int mid = ( lf + rg ) >> 1 ;
        if( check( mid ) ){
            ans = mid ;
            rg = mid - 1 ;
        } else lf = mid + 1 ;
    }
    if( ans == -1 ) puts( "NIE" ) ;
    else printf( "%d" , ans ) ;
}

int main(){
    scanf( "%d%d" , &N , &M ) ;
    for( int i = 1 ; i <= M ; i ++ )
        scanf( "%d%d%d%d" , &e[i].u , &e[i].v , &e[i].Uv , &e[i].Vu ) ;
    solve() ;
}

你可能感兴趣的:(二分答案,网络流)