說在前面
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() ;
}