[BZOJ5335]-[TJOI2018]智力竞赛-二分答案+可重路径覆盖

说在前面

md笔记本键盘真难用…
到了林荫校区了qwq,感觉很棒!


题目

BZOJ5335传送门
看题可戳传送门


解法

me觉得这个题面描述十分不清真
题意就是让求可重最小链覆盖,但是题目上并没有说有没有环,所以正常写法就是先tarjan,然后跑一遍floyd处理连通性,最后二分答案check

然而me觉得出题人不可能强行tarjan,于是把LOJ的数据全部下载下来check了一遍,发现全是DAG= =???

这就很有意思了…


下面是自带大常数的代码

me也不知道为啥,反正跑的慢

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

int N , M , head[505] , tp ;
bool acce[505][505] ;
struct Data{
    int id , val ;
    bool operator < ( const Data &A ) const {
        return val < A.val ;
    }
} d[505] ;
struct Path{
    int pre , to ;
} p[250005] ;

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

void preWork(){
    sort( d + 1 , d + M + 1 ) ;
    for( int k = 1 ; k <= M ; k ++ )
        for( int i = 1 ; i <= M ; i ++ ) if( i != k )
            for( int j = 1 ; j <= M ; j ++ ) if( j != k && j != i )
                if( !acce[i][j] ) acce[i][j] = ( acce[i][k] & acce[k][j] ) ;
    for( int i = 1 ; i <= M ; i ++ )
        for( int j = 1 ; j <= M ; j ++ )
            if( i != j && acce[i][j] ) In( i , j ) ;
}

int mat[505] ;
bool vis[505] , ban[505] ;
bool match( int u ){
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( vis[v] || ban[v] ) continue ;
        vis[v] = true ;
        if( !mat[v] || match( mat[v] ) ){
            mat[v] = u ;
            return true ;
        }
    } return false ;
}

bool check( int mid ){
    int cnt = 0 ;
    memset( ban , 0 , sizeof( ban ) ) ;
    memset( mat , 0 , sizeof( mat ) ) ;
    for( int i = mid + 1 ; i <= M ; i ++ ) ban[ d[i].id ] = true ;
    for( int i = 1 ; i <= M ; i ++ ){
        if( ban[i] ) continue ;
        memset( vis , 0 , sizeof( vis ) ) ;
        cnt += match( i ) ;
    } return mid - cnt <= N + 1 ;
}

void solve(){
    int lf = 1 , rg = M - 1 , mid , ans ;
    if( check( M ) ) puts( "AK" ) , exit( 0 ) ;
    while( lf <= rg ){
        mid = ( lf + rg ) >> 1 ;
        if( check( mid ) )
            ans = mid , lf = mid + 1 ;
        else rg = mid - 1 ;
    } printf( "%d\n" , d[ans+1].val ) ;
}

int main(){
    scanf( "%d%d" , &N , &M ) ;
    for( int i = 1 , son ; i <= M ; i ++ ){
        scanf( "%d" , &d[i].val ) , d[i].id = i ;
        scanf( "%d" , &son ) ;
        for( int j = 1 , v ; j <= son ; j ++ )
            scanf( "%d" , &v ) , acce[i][v] = true ;
    } preWork() ; solve() ;
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值