题目大意
5000留念
现在有一副若干条边的二分图,左边有
N
个点 ,右边有
- 选出的点权和大于等于限制t
- 并且可以从图中选出若干条边,使得二分图中每个点最多被一条边覆盖,而选出的点要恰好被一条边覆盖。
求有多少个满足条件的子图。
Data Constraint
n,m≤20
题解
这题要用到霍尔定理。
Hall定理
简单讲一下我个人的理解。
对于一个二分图具备完备匹配的充要条件是对于左边点的每一个子集,和子集内的点相连的点集大小大于等于子集大小。
假如说只选出一遍的点的子集,我们显然可以Hall定理判断。实际上两边如果都满足,那么这个结论还是成立的。
所以我们只需要分别找出两边合法的方案,然后two pointers扫一下就解决了。
时间复杂度: O(2n∗n)
SRC
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std ;
#define N 40 + 10
#define M 2000000 + 10
typedef long long ll ;
bool f[2][M] ;
ll Rec1[M] , Rec2[M] ;
int W[N] , G[2][N] , Num[M] ;
int n , on , m , T , Size ;
ll ans ;
void link1( int u , int v ) {
G[0][u] |= 1 << (v - 1) ;
}
void link2( int u , int v ) {
G[1][u] |= 1 << (v - 1) ;
}
int Count( int s ) {
int ret = 0 ;
while ( s ) {
ret ++ ;
s -= s & (-s) ;
}
return ret ;
}
void Solve( int n , int t ) {
for (int s = 0 ; s < (1 << n) ; s ++ ) {
f[t][s] = 1 ;
int st = 0 ;
ll sum = 0 ;
for (int i = 1 ; i <= n ; i ++ ) {
if ( (s >> (i - 1)) & 1 ) {
int _s = s ^ (1 << (i - 1)) ;
if ( !f[t][_s] ) { f[t][s] = 0 ; break ; }
st |= G[t][i] ;
sum += W[t*on+i] ;
}
}
if ( Num[st] < Num[s] ) f[t][s] = 0 ;
if ( f[t][s] ) {
if ( t == 0 ) Rec1[++Rec1[0]] = sum ;
else Rec2[++Rec2[0]] = sum ;
}
}
}
int main() {
freopen( "guard.in" , "r" , stdin ) ;
freopen( "guard.out" , "w" , stdout ) ;
scanf( "%d%d" , &n , &m ) ;
for (int i = 1 ; i <= n ; i ++ ) {
scanf( "\n" ) ;
for (int j = 1 ; j <= m ; j ++ ) {
char c ;
scanf( "%c" , &c ) ;
if ( c == '1' ) link1( i , j ) , link2( j , i ) ;
}
}
for (int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &W[i] ) ;
for (int i = 1 ; i <= m ; i ++ ) scanf( "%d" , &W[n+i] ) ;
scanf( "%d" , &T ) ;
for (int s = 0 ; s < (1 << max(n,m)) ; s ++ ) Num[s] = Count(s) ;
on = n ;
Solve( n , 0 ) ;
Solve( m , 1 ) ;
sort( Rec1 + 1 , Rec1 + Rec1[0] + 1 ) ;
sort( Rec2 + 1 , Rec2 + Rec2[0] + 1 ) ;
int j = Rec2[0] + 1 ;
for (int i = 1 ; i <= Rec1[0] ; i ++ ) {
while ( j > 1 && Rec2[j-1] + Rec1[i] >= T ) j -- ;
if ( Rec2[j] + Rec1[i] >= T ) ans += Rec2[0] - j + 1 ;
}
printf( "%lld\n" , ans ) ;
return 0 ;
}
以上.