D. 0-1 MST
题意:给一个无向图,如果
u
,
v
u,v
u , v 有边相连,那么他们的边权为1,否则为0,求最小生成树
解法:不难发现最小生成树权值其实可以转化为该无向图的补图的联通块个数减一,那我们就来求补图联通块好了,我们用一个
s
e
t
set
s e t 存所有没有用过的点,我们开始任意选择一个
s
e
t
set
s e t 里的点开始
b
f
s
bfs
b f s ,假设出队的点为
u
u
u ,我们枚举
s
e
t
set
s e t 的点
v
v
v ,如果
u
,
v
u,v
u , v 没有边,那么
u
,
v
u,v
u , v 就是一个联通块,把
v
v
v 加入队列即可,并且从
s
e
t
set
s e t 中删除
v
v
v 即可,如果有边,则继续枚举即可
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 10 ;
int n, vis[ maxn] , cnt[ maxn] , res;
set< int > s, G[ maxn] ;
queue< int > q;
void bfs ( int u)
{
vis[ u] = 1 ;
s. erase ( u) ;
q. push ( u) ;
while ( ! q. empty ( ) )
{
u = q. front ( ) ;
q. pop ( ) ;
set< int > : : iterator it;
for ( it = s. begin ( ) ; it != s. end ( ) ; )
{
int v = * it;
++ it;
if ( G[ u] . find ( v) == G[ u] . end ( ) )
{
s. erase ( v) ;
vis[ v] = 1 ;
q. push ( v) ;
}
}
}
}
int main ( )
{
int m, u, v, ans = 0 ;
scanf ( "%d%d" , & n, & m) ;
for ( int i = 1 ; i <= n; i++ )
s. insert ( i) ;
for ( int i = 1 ; i <= m; i++ )
{
scanf ( "%d%d" , & u, & v) ;
G[ u] . insert ( v) ;
G[ v] . insert ( u) ;
}
for ( int i = 1 ; i <= n; i++ )
if ( ! vis[ i] )
{
ans++ ;
bfs ( i) ;
}
printf ( "%d\n" , ans - 1 ) ;
}
E. Sum Balance
题意:有
n
n
n 个数字集合(所有数都不同),你要从每个集合取出一个数字,然后重新分配给每个集合一个数字,使得每个集合的数字和相等,输出选择方案。
解法:我们先求出平均每个集合数字和
s
u
m
sum
s u m ,然后把每个数字看成一个点,枚举每个数字
a
i
j
a_{ij}
a i j ,假设第
i
i
i 个集合的数字和为
s
i
s_{i}
s i ,如果我把
a
i
j
a_{ij}
a i j 取出去,那么需要补充
v
a
l
=
s
u
m
−
(
s
i
−
a
i
j
)
val = sum-(s_{i}-a_{ij})
v a l = s u m − ( s i − a i j ) ,如果
v
a
l
val
v a l 存在,且
v
a
l
val
v a l 不在
i
i
i 集合或者
v
a
l
=
a
i
j
val=a_{ij}
v a l = a i j ,那么我们可以连一条有向边
a
i
j
−
>
v
a
l
a_{ij}->val
a i j − > v a l ,问题转化成了
n
n
n 个集合能否组成一个或多个不相交的环,由于
n
<
=
15
n<=15
n < = 1 5 ,我们可以先暴力找到所有环包含的集合,然后用状压
d
p
dp
d p 去解即可。
#include <bits/stdc++.h>
#define pi pair<int, int>
#define mk make_pair
#define ll long long
using namespace std;
const int maxn = 5001 * 15 ;
unordered_map< ll, int > mp;
vector< pi> ans[ 1 << 15 ] ;
int cnt, dp[ 1 << 15 ] ;
int k[ 16 ] , a[ 16 ] [ 5001 ] , vis[ 16 ] , G[ maxn] ;
ll s[ 16 ] ;
pi p[ maxn] ;
void gao ( int jie)
{
memset ( vis, 0 , sizeof ( vis) ) ;
queue< int > q;
vector< pi> tmp;
int S = ( 1 << p[ jie] . first - 1 ) ;
q. push ( jie) ;
while ( ! q. empty ( ) )
{
int u = q. front ( ) ;
q. pop ( ) ;
if ( u == jie && vis[ p[ u] . first] )
{
dp[ S] = 1 ;
ans[ S] = tmp;
return ;
}
if ( vis[ p[ u] . first] )
return ;
vis[ p[ u] . first] = 1 ;
if ( G[ u] )
{
int v = G[ u] ;
q. push ( v) ;
S | = ( 1 << p[ v] . first - 1 ) ;
tmp. push_back ( mk ( a[ p[ v] . first] [ p[ v] . second] , p[ u] . first) ) ;
}
}
}
bool cmp ( pi A, pi B)
{
int p1 = mp[ A. first] ;
int p2 = mp[ B. first] ;
return p[ p1] . first < p[ p2] . first;
}
int main ( )
{
int n;
scanf ( "%d" , & n) ;
ll sum = 0 ;
for ( int i = 1 ; i <= n; i++ )
{
scanf ( "%d" , & k[ i] ) ;
for ( int j = 1 ; j <= k[ i] ; j++ )
{
scanf ( "%d" , & a[ i] [ j] ) ;
sum + = a[ i] [ j] ;
s[ i] + = a[ i] [ j] ;
mp. emplace ( a[ i] [ j] , ++ cnt) ;
p[ cnt] = mk ( i, j) ;
}
}
if ( sum % n)
return puts ( "No" ) , 0 ;
sum / = n;
for ( int i = 1 ; i <= n; i++ )
for ( int j = 1 ; j <= k[ i] ; j++ )
{
ll v = sum - ( s[ i] - a[ i] [ j] ) ;
int cur = mp[ v] ;
if ( ! cur || ( p[ cur] . first == i && p[ cur] . second != j) )
continue ;
G[ mp[ a[ i] [ j] ] ] = ( cur) ;
}
for ( int i = 1 ; i <= cnt; i++ )
gao ( i) ;
int S = ( 1 << n) - 1 ;
for ( int i = 1 ; i <= S; i++ )
if ( ! dp[ i] )
{
for ( int j = ( i - 1 ) & i; j; j = ( j - 1 ) & i)
if ( dp[ j] && dp[ i ^ j] )
{
dp[ i] = 1 ;
ans[ i] = ans[ j] ;
for ( auto tmp : ans[ i ^ j] )
ans[ i] . push_back ( tmp) ;
break ;
}
}
if ( dp[ S] == 0 )
return puts ( "No" ) , 0 ;
puts ( "Yes" ) ;
sort ( ans[ S] . begin ( ) , ans[ S] . end ( ) , cmp) ;
for ( auto tmp : ans[ ( 1 << n) - 1 ] )
printf ( "%d %d\n" , tmp. first, tmp. second) ;
}