[UOJ#171]-[wc2016]挑战NPC-带花树+建图

说在前面

并没有什么想说的,但是要保持格式=w=


题目

UOJ#171传送门
BZOJ4405传送门
看题可戳UOJ的传送门


解法

这道题建图真的好巧(反正me不会)
一开始me一直觉得这就是个二分图,然而怎么建图都没办法跑答案
然后去查了题解,得知建图是这样的:

我们让第 i 个筐对答案的贡献恰好为 [ m a t c h i 1 ]
就这样建:把一个筐拆成 3 个点,三个点之间互相连边形成三元环。对于一个球,如果可以放进这个筐,就向这个筐的三个点都连一条边
这样跑最大匹配,显然有 = [ m a t c h 1 ]
然后因为要输出方案,所以先匹配球,再让筐和筐匹配

就做完了,感觉很厉害…然而并不知道如何才能想到这种建图


下面是代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

int T , N , M , E , head[605] , tp ;
int buc[105][3] , ball[305] , id_c , rev[605] ;
struct Path{
    int pre , to ;
} p[ 2*(100*300*3*2 + 100*2) + 1000 ] ;

void In( int t1 , int t2 ){
    p[++tp] = ( Path ){ head[t1] , t2 } , head[t1] = tp ;
    p[++tp] = ( Path ){ head[t2] , t1 } , head[t2] = tp ;
}

int mat[605] , typ[605] , pre[605] ;
int fa[605] , que[605] , fr , ba , ans , tim[605] , tim_c ;
void init(){
    id_c = tp = ans = 0 ;
    memset( head , 0 , sizeof( head ) ) ;
    memset( mat , 0 , sizeof( mat ) ) ;
    for( int i = 1 ; i <= N ; i ++ ) ball[i] = ++id_c , rev[id_c] = 0 ;
    for( int i = 1 ; i <= M ; i ++ ){
        for( int j = 0 ; j < 3 ; j ++ )
            buc[i][j] = ++id_c , rev[id_c] = i ;
        In( buc[i][0] , buc[i][1] ) , In( buc[i][1] , buc[i][2] ) ;
    }
}

int find( int x ){
    if( fa[x] == x ) return x ;
    return fa[x] = find( fa[x] ) ;
}

int getLca( int u , int v ){
    tim_c ++ , u = find( u ) , v = find( v ) ;
    while( true ){
        if( u ){
            if( tim[u] == tim_c ) return u ;
            tim[u] = tim_c , u = find( pre[ mat[u] ] ) ;
        } swap( u , v ) ;
    }
}

void blossom( int u , int v , int lca ){
    for( ; find( u ) != lca ; ){
        pre[u] = v , fa[u] = fa[ mat[u] ] = lca ;
        v = mat[u] , u = pre[v] ;
        if( typ[v] == 1 ) typ[v] = 0 , que[++ba] = v ;
    }
}

bool BFS( int St ){
    for( int i = 1 ; i <= id_c ; i ++ )
        typ[i] = -1 , fa[i] = i ;
    fr = 1 , typ[St] = 0 , que[ ba = 1 ] = St ;
    while( ba >= fr ){
        int u = que[fr++] ;
        for( int i = head[u] ; i ; i = p[i].pre ){
            int v = p[i].to ;
            if( typ[v] == -1 ){
                pre[v] = u , typ[v] = 1 ;
                if( !mat[v] ){
                    for( ; v ; u = pre[v] )
                        mat[v] = u , swap( mat[u] , v ) ;
                    return true ;
                } else typ[ mat[v] ] = 0 , que[++ba] = mat[v] ;
            } else if( !typ[v] && find( u ) != find( v ) ){
                int Lca = getLca( u , v ) ;
                blossom( u , v , Lca ) ;
                blossom( v , u , Lca ) ;
            }
        }
    } return false ;
}

void solve(){
    for( int i = 1 ; i <= N ; i ++ )
        if( !mat[ ball[i] ] ) ans += BFS( ball[i] ) ;
    for( int i = 1 ; i <= M ; i ++ )
        for( int j = 0 ; j < 3 ; j ++ )
            if( !mat[ buc[i][j] ] ) ans += BFS( buc[i][j] ) ;
    printf( "%d\n" , ans - N ) ;
    for( int i = 1 ; i <= N ; i ++ )
        printf( "%d " , rev[ mat[ball[i]] ] ) ;
    puts( "" ) ;
}

int main(){
    scanf( "%d" , &T ) ;
    while( T -- ){
        scanf( "%d%d%d" , &N , &M , &E ) , init() ;
        for( int i = 1 , u , v ; i <= E ; i ++ ){
            scanf( "%d%d" , &u , &v ) ;
            for( int j = 0 ; j < 3 ; j ++ )
                In( ball[u] , buc[v][j] ) ;
        } solve() ;
    }
}

猜你喜欢

转载自blog.csdn.net/izumi_hanako/article/details/80352678