• 【2017 Multi-University Training Contest


    【链接】点击打开链接


    【题意】


    给你一棵n节点的树,现在让你放k个猴子,可以删边,问最少可以剩余几条边,放k个猴子,满足任意一个猴

    至少与一只猴子相连。2<=k<=n<=1e5



    【题解】


    一条边最划算的做法就是,边的两个端点连的是两个单独的不同的点
    这两个点都没有和其他点相连
    而原题意可以理解为,用最少的边凑够k只猴子.
    上面说的这种做法,每次可以凑2只猴子
    显然是最优的。
    于是我们优先做这样的连法
    其实也就是在这棵树上选最多的相邻点对数
    树肯定是二分图
    相邻的点->二分图的两个部分
    会发现我们正是要找这个树的二分图最大匹配数
    二分图的最大匹配数=最小点覆盖.
    树的最小点覆盖是可以用O(N)的动规写出来的
    然后就知道有多少条边是可以一条边就凑两只猴子的了。
    剩下的如果猴子不够的话,就只能用一条边了。
    因为是两个两个地凑,奇数的话,那单独的一只只能额外加一条边了。
    要用输入挂(fread)不然会超时。

    【错的次数】


    0

    【反思】


    树肯定是二分图。
    相邻点对->二分图匹配。
    二分图匹配->点覆盖。

    【代码】

    /*
    
    */
    #include <cstdio>
    #include <iostream>
    #include <algorithm>
    #include <cstring>
    #include <vector>
    #include <map>
    #include <queue>
    #include <iomanip>
    #include <set>
    #include <cstdlib>
    #include <cmath>
    using namespace std;
    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    #define LL long long
    #define rep1(i,a,b) for (int i = a;i <= b;i++)
    #define rep2(i,a,b) for (int i = a;i >= b;i--)
    #define mp make_pair
    #define pb emplace_back
    #define fi first
    #define se second
    #define ld long double
    #define ms(x,y) memset(x,y,sizeof x)
    #define ri(x) scanf("%d",&x)
    #define rl(x) scanf("%lld",&x)
    #define rs(x) scanf("%s",x)
    #define rf(x) scnaf("%lf",&x)
    #define oi(x) printf("%d",x)
    #define ol(x) printf("%lld",x)
    #define oc putchar(' ')
    #define os(x) printf(x)
    #define all(x) x.begin(),x.end()
    #define Open() freopen("F:\rush.txt","r",stdin)
    #define Close() ios::sync_with_stdio(0)
    #define sz(x) ((int) x.size())
    #define ld long double
    
    typedef pair<int, int> pii;
    typedef pair<LL, LL> pll;
    
    //mt19937 myrand(time(0));
    //int get_rand(int n){return myrand()%n + 1;}
    const int dx[9] = { 0,1,-1,0,0,-1,-1,1,1 };
    const int dy[9] = { 0,0,0,-1,1,-1,1,-1,1 };
    const double pi = acos(-1.0);
    const int N = 1e5;
    
    namespace IO {
    	const int MX = 4e7;//随输入量改变
    	char buf[MX]; int c, sz;
    	void begin() {
    		c = 0;
    		sz = fread(buf, 1, MX, stdin);
    	}
    	inline bool read(int &t) {
    		while (c < sz && buf[c] != '-' && (buf[c] < '0' || buf[c] > '9')) c++;
    		if (c >= sz) return false;
    		bool flag = 0; if (buf[c] == '-') flag = 1, c++;
    		for (t = 0; c < sz && '0' <= buf[c] && buf[c] <= '9'; c++) t = t * 10 + buf[c] - '0';
    		if (flag) t = -t;
    		return true;
    	}
    }
    
    int n, k,dp1[N+10],dp2[N+10];//放 不放
    vector <int> g[N + 10];
    
    void dfs(int x) {
        dp1[x] = 1,dp2[x] = 0;
        int len = g[x].size();
        rep1(i,0,len-1){
            int y = g[x][i];
            dfs(y);
            dp1[x]+=min(dp1[y],dp2[y]);
            dp2[x]+=dp1[y];
        }
    }
    
    int main() {
    	//Open();
    	//Close();
    	int T;
    	IO::begin();
    	IO::read(T);
    	while (T--) {
    		IO::read(n), IO::read(k);
    		rep1(i,1,n) g[i].clear();
    		rep1(i, 1, n - 1) {
    			int x;
    			IO::read(x);
    			g[x].pb(i+1);
    		}
    		dfs(1);
            int dui = min(dp1[1],dp2[1]);
            int flag = k&1;
            k/=2;
            if (k <= dui){
                oi(k + flag);puts("");
            }else{
                //k>dui
                oi(dui + k*2 - dui*2 + flag);puts("");
            }
    	}
    	return 0;
    }
    


  • 相关阅读:
    072孤荷凌寒从零开始学区块链第72天DAPP027
    014自学方法论_养成随手记录自学过程遇到问题的好习惯
    015把注意力放在养大我们金鹅身上
    071孤荷凌寒从零开始学区块链第71天DAPP026
    070孤荷凌寒从零开始学区块链第70天DAPP024
    068孤荷凌寒从零开始学区块链第68天DAPP022
    014什么才是一个人最宝贵的资产
    013自学方法论_怎样才是最高效率加强记忆的自学
    012自学方法谈_不要依赖视频,培养自己的阅读理解能力
    013学会建立一个个自动化的管道
  • 原文地址:https://www.cnblogs.com/AWCXV/p/7626065.html
Copyright © 2020-2023  润新知