题目描述
原题面等比赛结束后才能复制,现在只能大概用中文简述一下了。
给你
N
N
只鸭子、条使用中的船以及
K
K
条未使用的船,条船两边连接着两只不同的鸭子
i,j
i
,
j
,每只鸭子只有当不少于2条船支撑时才不会沉下去。如果一旦一只鸭子沉没了,它所连带的船也会一起沉没,问如果用上
K
K
条闲置的船,最少有多少只鸭子再也漂不起来。
数据范围
最多有100组数据,其中
分析
1.首先想一下,其实船不需要作为点连到整个图中,只需要将鸭子作为点连入即可(随便想一想就可以了)。
2.对于所有初始的状态,我们可以提前将所有应该沉下去的鸭子提前处理出来,只需要一遍
O(N)
O
(
N
)
。
3.建立一个虚点
N+1
N
+
1
连向所有沉没的鸭子,方便后面答案的统计,注意点
N+1
N
+
1
的状态。
4.yy一个简单的贪心,对于每条闲置的船,如果把整个鸭子连成的图想象成一个类树一样的东西,我们每次需要浮起来的总是树上沉下去的最长链,所以我们可以把每个点所能拉起的最长链长度记下来,用priority_queue来维护,确保船的最大效用。
5.对于最长链,直接一遍dfs,更新节点深度的时候保证每次其深度都是由最深的节点转移而来,这样确保链最长。统计答案的时候还要注意一下可能存在的侧链,假如鸭子
u,v
u
,
v
之间存在一条边,但
v
v
不在u的最长链上,那么我们统计答案的时候把切开,这样确保主链的完整。
#include <bits/stdc++.h>
#define rep( i , l , r ) for( int i = (l) ; i <= (r) ; ++i )
#define per( i , r , l ) for( int i = (r) ; i >= (l) ; --i )
#define erep( i , u ) for( int i = head[(u)] ; ~i ; i = e[i].nxt )
using namespace std;
int _read(){
char ch = getchar();
int x = 0 , f = 1 ;
while( !isdigit( ch ) )
if( ch == '-' ) f = -1 , ch = getchar();
else ch = getchar();
while( isdigit( ch ) )
x = (ch - '0') + x * 10 , ch = getchar();
return x * f;
}
const int maxn = 20000 + 5;
struct edge{
int v , nxt;
} e[maxn * 3];
int head[maxn] , _t = 0;
inline void addedge( int u , int v ){
e[_t].v = v , e[_t].nxt = head[u] , head[u] = _t++;
}
int D[maxn] , d[maxn];
bool vis[maxn] , alive[maxn];
void sink( int u ){
if( d[u] > 1 || alive[u] == 0 ) return;
alive[u] = 0;
erep( i , u ){
int v = e[i].v;
d[v]--; sink( v );
}
}
int dep[maxn];
inline int _max( int a , int b ){ return a > b ? a : b; }
void dfs( int u , int fa ){
dep[u] = 1;
erep( i , u ){
int v = e[i].v;
if( v == fa || alive[v]) continue;
dfs( v , u );
dep[u] = _max( dep[u] , dep[v] + 1 );
}
}
int cnt[maxn];
void calc( int u , int fa ){
bool flg = 1;
erep( i , u ){
int v = e[i].v;
if( v == fa || alive[v] ) continue;
if( dep[v] + 1 == dep[u] && flg ) cnt[v] = cnt[u] + 1 , flg = 0;
else cnt[v] = 1;
calc( v , u );
}
}
priority_queue<int> q;
int main(){
int T = _read();
while( T-- ){
memset( D , 0 , sizeof D );
memset( head , 0xff , sizeof head );
_t = 0;
int N = _read() , M = _read() , K = _read();
int u , v;
rep( i , 1 , M ){
u = _read() , v = _read();
addedge( u , v );
addedge( v , u );
D[u]++; D[v]++;
}
memcpy( d , D , sizeof D );
rep( i , 1 , N ) alive[i] = 1;
rep( i , 1 , N ) sink( i );
alive[N + 1] = 0;
int ans = N;
rep( u , 1 , N )
if( alive[u] ){
erep( i , u )
if( !alive[e[i].v] ) addedge( N + 1 , e[i].v );
ans--;
}
dfs( N + 1 , -1 );
cnt[N + 1] = 0;
calc( N + 1 , -1 );
while( !q.empty() ) q.pop();
rep( i , 1 , N )
if( D[i] == 1 ) q.push( cnt[i] );
rep( i , 1 , K ){
if( q.empty() ) break;
ans -= q.top(); q.pop();
}
cout << ans << endl;
}
return 0;
}