题目大意:
有
N
N
N个机房,机房
A
i
A_i
Ai和机房
i
+
1
i+1
i+1相连,把
K
K
K只企鹅安排到不同机房,要保证每只企鹅至少还能通过留下来的网线和至少另一只企鹅联机游戏。
问最少需要保留多少根网线。
数据有
T
T
T组。
2 ≤ K ≤ N ≤ 100000 , T ≤ 10 2≤K≤N≤100000,T≤10 2≤K≤N≤100000,T≤10
分析:
要更优显然就要尽可能选出多的边满足两边都有企鹅,且企鹅仅有这一条边可以联系其他企鹅,设这样的边有
x
x
x个
当
x
∗
2
>
=
k
x*2>=k
x∗2>=k时,显然答案就是
a
+
1
2
\frac{a+1}{2}
2a+1
而当
x
∗
2
<
k
x*2<k
x∗2<k时,显然是选了
x
x
x条边中,然后通过
(
k
−
x
∗
2
)
(k-x*2)
(k−x∗2)条边连接剩下的点,答案为
x
+
(
k
−
x
∗
2
)
x+(k-x*2)
x+(k−x∗2)
怎么求这样的边的数量呢,可以用到树形dp,
设
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]表示以
i
i
i为根的子树不含节点
i
i
i中这样的边的数量。
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]表示以
i
i
i为根的子树中含节点
i
i
i这样的边的数量。
然后对于每个节点
i
i
i,都有
d
p
[
i
]
[
0
]
=
Σ
j
=
1
n
d
p
[
j
]
[
1
]
(
j
为
u
儿
子
)
dp[i][0]=Σ_{j=1}^{n}dp[j][1](j为u儿子)
dp[i][0]=Σj=1ndp[j][1](j为u儿子)
求出了
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]以后,可以求出
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]
d
p
[
i
]
[
1
]
=
m
a
x
(
d
p
[
i
]
[
1
]
,
d
p
[
i
]
[
0
]
−
d
p
[
s
o
n
]
[
1
]
+
d
p
[
s
o
n
]
[
0
]
+
1
)
dp[i][1]=max(dp[i][1],dp[i][0]-dp[son][1]+dp[son][0]+1)
dp[i][1]=max(dp[i][1],dp[i][0]−dp[son][1]+dp[son][0]+1)
那么
x
x
x就是
m
a
x
(
d
p
[
1
]
[
0
]
,
d
p
[
1
]
[
1
]
)
max(dp[1][0],dp[1][1])
max(dp[1][0],dp[1][1])
代码:
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<vector>
#include<cmath>
#include<queue>
#define N 100005
using namespace std;
struct Node { int To, nxt; }e[2*N];
int f[N][2], ls[N], T, n, k, cnt, ans;
void Addedge(int u, int v)
{
e[++cnt].To = v, e[cnt].nxt = ls[u], ls[u] = cnt;
}
void dfs(int x, int father)
{
f[x][0] = f[x][1] = 0;
for (int i = ls[x]; i; i = e[i].nxt)
{
if (e[i].To == father) continue;
int y = e[i].To;
dfs(y, x);
f[x][0] += f[y][1];
}
for (int i = ls[x]; i; i = e[i].nxt)
{
if (e[i].To == father) continue;
int y = e[i].To;
f[x][1] = max(f[x][1], f[x][0] - f[y][1] + f[y][0] + 1);
}
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d", &T);
while (T--)
{
scanf("%d %d", &n, &k);
memset(f, 0, sizeof(f));
memset(ls, 0, sizeof(ls));
cnt = 0;
for (int i = 2; i <= n; i++)
{
int x;
scanf("%d", &x);
Addedge(i, x);
Addedge(x, i);
}
dfs(1, -1);
ans = max(f[1][0], f[1][1]);
if (ans * 2 >= k) printf("%d\n", k / 2 + k % 2);
else printf("%d\n", ans + (k - 2 * ans));
}
return 0;
}