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

来源:互联网 发布:java nanotime 编辑:程序博客网 时间:2024/05/24 23:15

说在前面

写这道题顺便学了学混合图欧拉回路判定,感觉自己萌萌哒!
话说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 <cstdio>#include <cstring>#include <algorithm>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() ;}