zoukankan      html  css  js  c++  java
  • 题解 P4107 【[HEOI2015]兔子与樱花】

    思路好想,卡常不好卡

    题目链接

    Solution [HEOI2015]兔子与樱花

    题目大意:给定一个树,每个节点有一个权值。如果删除一个节点的话,就将它的权值加到它父节点上,并将它的儿子接到父节点上。要求在任意时刻每个节点的权值与儿子个数和小于常数(m),求最多可以删去多少个节点

    贪心


    分析:

    比较显然的做法是,我们考虑一个节点的”危害":即删去它后,父节点的权值与儿子个数和会增加多少

    危害值:(w[x]=c[x]+son[x]-1)

    那么我们把危害值从小到大排序,贪心能删就删即可

    删除一个节点同时会影响它父亲的危害值,所以你大可以手打斐波那契堆,事实上有一个更好的做法,我们优先删除深度大的节点。也就是按深度一层层贪心。有几点性质保证正确性

    • 1.如果先删除父节点再删除子节点合法,那么反过来一定合法

    证明:设节点(x),父节点(f),祖父节点(ff)

    先删除父亲满足

    (c[x]+c[f]+c[ff]+son[ff]-1+son[f]-1+son[x] leq m)

    先删除子节点需要额外满足

    (c[x]+c[f]+son[f]-1+son[x] leq m)

    (ecause son[ff] geq 1)

    ( herefore c[ff]+son[ff]-1 geq 0)

    得证

    • 2.如果一个节点在第一次处理它时没有被删去,那么它以后也不可能再被删去了

    如果将该节点的危害值加给它父亲不合法,那么如果要在以后删除它,哪怕它父亲、它祖父只有(1)个儿子,把它危害值加给它祖父也一定不合法了((c[x]+son[x])单调不降)

    所以我们一层层贪心就可以了,删不了的就直接丢掉不管,为了优化可以写成(dfs)的形式。回溯的时候计算就可以了(兄弟节点互不影响)

    #include <cstdio>
    #include <cctype>
    #include <vector>
    #include <list>
    #include <algorithm>
    using namespace std;
    const int maxn = 2e6 + 100;
    namespace FastIO{
    	const int bufsiz = 1 << 22;
    	char buf[bufsiz];
    	inline char getch(){
    		static int tot;
    		tot++;
    		if(tot == bufsiz){
    			fread(buf,1,bufsiz,stdin);
    			tot = 0;
    		}
    		return buf[tot];
    	}
    	inline int read(){
    		int x = 0;char c = getch();
    		while(!isdigit(c))c = getch();
    		while(isdigit(c))x = x * 10 + c - '0',c = getch();
    		return x;
    	}
    }using FastIO::read;
    vector<int> vec[maxn];
    int c[maxn],son[maxn],head[maxn],nxt[maxn],to[maxn],node[maxn],l[maxn],r[maxn],n,m,ans,tot;
    inline void addedge(int from,int to){
    	static int tot;
    	::to[++tot] = to;
    	nxt[tot] = head[from];
    	head[from] = tot;
    }
    inline void dfs(int u = 1,int faz = -1){
    	for(int i = head[u];i;i = nxt[i]){
    		int v = to[i];
    		if(v == faz)continue;
    		dfs(v,u);
    	}
    	if(!son[u])return;
    	sort(node + l[u],node + r[u] + 1,[](int a,int b){return (son[a] + c[a]) < (son[b] + c[b]);});
    	for(int i = l[u];i <= r[u];i++)
    		if(c[u] + son[u] + c[node[i]] + son[node[i]] - 1 <= m){
    			ans++;
    			c[u] += c[node[i]];
    			son[u] += son[node[i]] - 1;
    		}
    }
    int main(){
    	// freopen("fafa.in","r",stdin);
    	n = read(),m = read();
    	for(int i = 1;i <= n;i++)c[i] = read();
    	for(int u = 1;u <= n;u++){
    		int k = son[u] = read();
    		if(!k)continue;
    		l[u] = tot + 1;
    		r[u] = tot + k;
    		while(k--){
    			int v = node[++tot] = read() + 1;
    			addedge(u,v);
    		}
    	}
    	dfs();
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    GB/T 38635.1-2020 信息安全技术 SM9标识密码算法 第1部分:总则
    信息安全行业国家标准汇总,信息安全行业从业人员必看
    贪吃蛇游戏(printf输出C语言版本)
    C 实战练习题目57
    C 实战练习题目56
    C 实战练习题目55
    C 实战练习题目54
    C 实战练习题目53
    C 实战练习题目52
    C 实战练习题目51
  • 原文地址:https://www.cnblogs.com/colazcy/p/13369561.html
Copyright © 2011-2022 走看看