1. 程式人生 > >JZOJ5455. 【NOIP2017提高A組衝刺11.6】拆網線

JZOJ5455. 【NOIP2017提高A組衝刺11.6】拆網線

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。

題解

如果所有企鵝都兩兩間用一條網線的話,
這個是最優的情況,
然而不是所有形態的樹都可以找到足夠的這種點對,比如說菊花圖。
如果在不夠這種點對的情況下,只能將剩下的企鵝每隻花費一條網線。

現在問題就轉變成為求出最多有多少對這種點對,
f

i,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; }