拆网线
题面
Description
企鹅国的网吧们之间由网线互相连接,形成一棵树的结构。现在由于冬天到了,供暖部门缺少燃料,于是他们决定去拆一些网线来做燃料。但是现在有K只企鹅要上网和别人联机游戏,所以他们需要把这K只企鹅安排到不同的机房(两只企鹅在同一个机房会吵架),然后拆掉一些网线,但是需要保证每只企鹅至少还能通过留下来的网线和至少另一只企鹅联机游戏。
所以他们想知道,最少需要保留多少根网线?
Input
第一行一个整数T,表示数据组数;
每组数据第一行两个整数N,K,表示总共的机房数目和企鹅数目。
第二行N-1个整数,第i个整数Ai表示机房i+1和机房Ai有一根网线连接(1≤Ai≤i)。
Output
每组数据输出一个整数表示最少保留的网线数目。
Sample Input
2
4 4
1 2 3
4 3
1 1 1
Sample Output
2
2
Data Constraint
对于30%的数据:N≤15;
对于50%的数据:N≤300;
对于70%的数据:N≤2000;
对于100%的数据:2≤K≤N≤100000,T≤10。
思路
30 % 30\% 30%数据做法
暴力搜索枚举删那些边,验证即可。
50 % 50\% 50%数据做法
设 f i , j , k f_{i,j,k} fi,j,k表示当前子树根节点为 i i i,与子树中 j j j个结点相连,是否安排了 k k k只企鹅。时间复杂度 O ( n 3 ) O(n^3) O(n3),预计得分: 50 50 50。
70 % 70\% 70%数据做法
考虑
D
P
DP
DP。
设
f
i
,
j
f_{i,j}
fi,j表示子树根节点
i
i
i与子树中
j
j
j个节点相连,最多安排了多少企鹅。时间复杂度
O
(
n
2
)
O(n^2)
O(n2),预计得分:70。
100 % 100\% 100%数据做法
仍旧是考虑
D
P
DP
DP,掺杂了一些贪心的思想。
一条边可以连接两个企鹅站,那么这样的一对点对越多越好。
如果有
p
p
p对点对,那么我们有:
若
p
≥
k
p\ge k
p≥k,我们只需要
⌈
k
2
⌉
\lceil \frac k2\rceil
⌈2k⌉条边 ;
否则,我们需要
p
2
+
(
k
−
p
)
\frac p2+(k-p)
2p+(k−p)条边。
那么问题就转换为求上述的
p
p
p了。
设
f
i
,
0
f_{i,0}
fi,0表示以
i
i
i为根的子树最大两两匹配数,不包含
i
i
i节点
f
i
,
1
~~~f_{i,1}
fi,1表示以
i
i
i为根的子树最大两两匹配数,包含
i
i
i节点
那么我们有转移方程:
f
i
,
0
=
∑
s
o
n
f
s
o
n
,
1
f_{i,0}=\sum_{son}f_{son,1}
fi,0=∑sonfson,1
f
i
,
1
=
m
a
x
(
f
i
,
1
,
f
i
,
0
−
f
s
o
n
,
1
+
f
s
o
n
,
0
+
2
)
f_{i,1}=max(f_{i,1},f_{i,0}-f_{son,1}+f_{son,0}+2)
fi,1=max(fi,1,fi,0−fson,1+fson,0+2)
Code
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int T,cnt,head[200005],next[200005],road[200005],f[100005][2];
int max(int a,int b){return a>b?a:b;}
void add(int u,int v)
{
next[++cnt]=head[u];
head[u]=cnt;
road[cnt]=v;
}
void dfs(int u,int from)
{
for(int i=head[u];i;i=next[i])
{
int v=road[i];
if(v==from) continue;
dfs(v,u);
f[u][0]+=f[v][1];
}
for(int i=head[u];i;i=next[i])
{
int v=road[i];
if(v==from) continue;
f[u][1]=max(f[u][1],f[u][0]-f[v][1]+f[v][0]+2);
}
}
int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
scanf("%d",&T);
while(T--)
{
int n,k,p;
memset(f,0,sizeof(f));
memset(head,0,sizeof(head));
memset(next,0,sizeof(next));
memset(road,0,sizeof(road));
cnt=0;
scanf("%d%d",&n,&k);
for(int i=1;i<n;i++)
{
int u;
scanf("%d",&u);
add(u,i+1),add(i+1,u);
}
dfs(1,0);
p=max(f[1][0],f[1][1]);
if(p>=k) printf("%d\n",(k+1)/2);
else printf("%d\n",p/2+(k-p));
}
}