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

87 篇文章 0 订阅
24 篇文章 0 订阅

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[N],to[N*2],ans,x,y,f[2][N];

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;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值