您的位置:首页 > 其它

JZOJ5455. 【NOIP2017提高A组冲刺11.6】拆网线

2017-11-06 22:02 441 查看

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。

题解

如果所有企鹅都两两间用一条网线的话,

这个是最优的情况,

然而不是所有形态的树都可以找到足够的这种点对,比如说菊花图。

如果在不够这种点对的情况下,只能将剩下的企鹅每只花费一条网线。

现在问题就转变成为求出最多有多少对这种点对,

设fi,0以i为根节点的子树的最大点对数,不包括i

设fi,1以i为根节点的子树的最大点对数,包括i

转移就很简单。

最后就分情况来讨论。

code

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define N 100003
#define mo 233333
#define ll long long
#define P putchar
using namespace std;

char ch;
void read(int& n)
{
n=0;
for(ch=getchar();ch<'0' || ch>'9';ch=getchar());
for(;'0'<=ch && ch<='9';n=(n<<3)+(n<<1)+ch-48,ch=getchar());
}

void write(int x)
{
if(x>9) write(x/10);
P(x%10+'0');
}

int n,m,T,tot,nxt[N*2],b
,to[N*2],ans,x,y,f[2]
;

void dfs(int x,int fa)
{
f[0][x]=f[1][x]=0;
for(int i=b[x];i;i=nxt[i])
if(to[i]!=fa)
{
dfs(to[i],x);
f[0][x]+=f[1][to[i]];
}
for(int i=b[x];i;i=nxt[i])
if(to[i]!=fa)f[1][x]=max(f[1][x],f[0][x]-f[1][to[i]]+f[0][to[i]]+2);
}

void ins(int x,int y)
{
nxt[++tot]=b[x];
to[tot]=y;
b[x]=tot;
}

int main()
{
freopen("tree.in","r",stdin);
freopen("tree.out","w",stdout);
read(T);
while(T--)
{
read(n);read(m);
memset(b,0,sizeof(b));
tot=0;
for(int i=1;i<n;i++)
read(x),ins(x,i+1),ins(i+1,x);
dfs(1,0);
ans=max(f[0][1],f[1][1]);
if(ans>=m)write((m+1)/2);else write(m-ans/2);
P('\n');
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  贪心算法