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

企鹅国的网吧们之间由网线互相连接,形成一棵树的结构。现在由于冬天到了,供暖部门缺少燃料,于是他们决定去拆一些网线来做燃料。但是现在有K只企鹅要上网和别人联机游戏,所以他们需要把这K只企鹅安排到不同的机房(两只企鹅在同一个机房会吵架),然后拆掉一些网线,但是需要保证每只企鹅至少还能通过留下来的网线和至少另一只企鹅联机游戏。
所以他们想知道,最少需要保留多少根网线?

今天考场上又zz了,只切了第二题qwq

第一题打了一个很显然不对的贪心,35pts   QwQ

后来一交流发现:树上二分图匹配啊!

怎么明明想到了要尽量多选没有公共端点的边但是没有想到匹配啊(这样下去我可能今年就要退役了)

好了正题开始

上面已经说出正解了,但是很显然我们不能真的去跑匈牙利

设f[i][0]表示在x的子树中,x没有被选择的情况下最多有多少对点是两两配对的

f[i][1]表示x被选择的情况,显然f[i][0]=Σf[v][1] ,f[i][1]=max{f[i][0]-f[v][1]+f[v][0]+2} {v∈son[i]}

让后,我们令ans=max(f[1][0],f[1][1])

若2ans>=k 那么答案就是(k+1)/2

否则就是ans+(k-2ans)(显然没有两两配对的点,可以通过加一条边来增加一个点(一换一)

#include<vector>
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,k,f[100010][2],ans=0; 
vector<int> G[100010];
void dp(int x,int p){
	f[x][0]=f[x][1]=0;
	for(int v,i=0,z=G[x].size();i<z;++i)
		if((v=G[x][i])!=p){
			dp(v,x);
			f[x][0]+=f[v][1];
		}
	for(int v,i=0,z=G[x].size();i<z;++i)
		if((v=G[x][i])!=p) f[x][1]=max(f[x][1],f[x][0]-f[v][1]+f[v][0]+1);
}
int vvv(){
	G[1].clear();
	scanf("%d%d",&n,&k);
	for(int x,i=2;i<=n;++i){
		G[i].clear();
		scanf("%d",&x);
		G[x].push_back(i);
	}
	dp(1,0); ans=max(f[1][0],f[1][1]);
	if(ans*2>=k) printf("%d\n",k+1>>1);
	else printf("%d\n",ans+(k-ans*2));
}
int main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	int T; for(scanf("%d",&T);T--;vvv());
}

转载于:https://www.cnblogs.com/Extended-Ash/p/7800607.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值