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
#include
#include
#include
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();iif((v=G[x][i])!=p){
			dp(v,x);
			f[x][0]+=f[v][1];
		}
	for(int v,i=0,z=G[x].size();iif((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());
}

你可能感兴趣的:(OI,图论,----匹配,--------二分图匹配,求解策略,----贪心,----动态规划,Jzoj)