zoukankan      html  css  js  c++  java
  • 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
    ",k+1>>1);
    	else printf("%d
    ",ans+(k-ans*2));
    }
    int main(){
    	freopen("tree.in","r",stdin);
    	freopen("tree.out","w",stdout);
    	int T; for(scanf("%d",&T);T--;vvv());
    }

  • 相关阅读:
    110. 平衡二叉树-前序遍历-简单
    207. 课程表-有向图判断有无环状-中等难度
    java错误集锦
    199. 二叉树的右视图-二叉树,dfs,从右往左遍历-中等难度
    114. 二叉树展开为链表-二叉树,dfs-中等难度
    236. 二叉树的最近公共祖先-中序遍历-中等难度
    Kendo ui 入门知识点
    Css样式压缩、美化、净化工具 源代码
    Linq与Lambda常用查询语法
    15个超强悍的CSS3圆盘时钟动画赏析
  • 原文地址:https://www.cnblogs.com/Extended-Ash/p/9477238.html
Copyright © 2011-2022 走看看