Problem
acm.hdu.edu.cn/showproblem.php?pid=6178
Meaning
要在一棵 n 个点的树上放 k 只猴子,然后删掉尽量多的边,使得删边后,每只猴子都至少和另外一只猴子相连,问最后剩下的边数。
Analysis
因为至少两只猴子相连,就贪心地把树拆成“点 - 边 - 点”这样的片段,成对地放猴子,如果还不够放,剩下的结点都是孤立的,要补边连上任意一对,每补一只猴子就加一条边。可以一次简单地深搜贪心拆树,或者一次简单的树型 DP。
- 这题的考点主要是读入外挂。模板来自这次多校第一场的最后一题的标程。(看这程序好像是只能读非负整数)
- 这个读入挂想在本地测试的时候,要用
freopen()
来输入重定向,直接用stdin
键盘输入是不行的;但交题的时候不用加重定向(除非题目说明)
Code
#include <cstdio>
#include <cstring>
using namespace std;
/*------- 开挂 -------*/
namespace fastIO {
#define BUF_SIZE 100000
// fread -> read
bool IOerror = 0;
char nc() {
static char buf[BUF_SIZE], *pl = buf + BUF_SIZE, *pr = buf + BUF_SIZE;
if(pl == pr) {
pl = buf;
pr = buf + fread(buf, 1, BUF_SIZE, stdin);
if(pr == pl) {
IOerror = 1;
return -1;
}
}
return *pl++;
}
inline bool blank(char ch) {
return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
}
void read(int &x) {
char ch;
while(blank(ch = nc()));
if(IOerror)
return;
for(x = ch - '0'; (ch = nc()) >= '0' && ch <= '9'; x = x * 10 + ch - '0');
}
#undef BUF_SIZE
};
using namespace fastIO;
/*------- 完结 -------*/
const int N = 100000;
int head[N+1], to[N<<1], nxt[N<<1];
void add_edge(int f, int t, int sz)
{
to[sz] = t;
nxt[sz] = head[f];
head[f] = sz;
}
bool paired[N+1];
int odd; // 落单的点数
void dfs(int v, int f)
{
for(int i = head[v]; ~i; i = nxt[i])
if(to[i] != f)
{
dfs(to[i], v);
if(!paired[to[i]]) // 子结点未被配对
{
if(paired[v]) // 当前结点已配对, 不能跟它配
++odd; // 子结点落单
else // 当前结点跟它配对
paired[v] = paired[to[i]] = true;
}
}
}
int main()
{
int T;
read(T);
for(int n, k, ans; T--; )
{
read(n);
read(k);
memset(head, -1, sizeof head);
for(int f = 2, t, sz = 0; f <= n; ++f)
{
read(t);
add_edge(f, t, sz++);
add_edge(t, f, sz++);
}
memset(paired, false, sizeof paired);
odd = 0;
dfs(1, 0);
odd += !paired[1]; // 树根是否落单
int num = (n - odd) / 2; // 成功配对的对数
if(2 * num < k) // 配对点数不足 k
ans = num + k - num * 2;
else if(k & 1) // k 是奇数
ans = k / 2 + 1;
else // k 是偶数
ans = k / 2;
printf("%d\n", ans);
}
return 0;
}