Luogu P4107 [HEOI2015] 兔子与樱花

题目链接:传送门

对于每个点来说,它的权重分为两部分,一部分是子树大小,一部分是自身的点权。很明显是要从叶子节点开始删除,因为只有从下往上处理我们才能知道这个点子树的所有情况。一个点的点权是不会消失的,只会转移到自己的上层节点上,所以说要想删除更多的节点,就要在 m m m的限制内按权值从小到大删除子节点。
在输入就可以时处理好每个点的权重,也就是 w [ i ] = s i z [ i ] + c [ i ] w[i]=siz[i]+c[i] w[i]=siz[i]+c[i]。之后从叶子节点开始向上删除。遍历一个点 f r fr fr的所有子节点 i i i,将它们按权重从小到大排序,对每个子节点 i i i,判断 w [ f r ] − 1 + w [ i ] < = m w[fr]-1+w[i]<=m w[fr]1+w[i]<=m,也就是是否可以删除这个子节点,从小到大排序能保证删除最多的子节点,若上式成立,则删除子节点 i i i,并更新 f r fr fr的权值 w [ f r ] w[fr] w[fr](少了一个子节点,权值的增加量为 w [ i ] w[i] w[i],总变化量为 w [ i ] − 1 w[i]-1 w[i]1)。

保存子节点可以用优先队列或者开数组实现,使用优先队列会 T T T最后一个点,可以写个快读或者开 O 2 O_2 O2

/*
* @Author: lyle
* @Date:   2023-10-03 11:25:46
* @Last Modified by:   lyle
* @Last Modified time: 2023-10-03 16:30:01
*/

#include 
#define A 2000010

using namespace std;
char Getchar() {
    static char buf[100010], *p1 = buf, *p2 = buf;
    return p1 == p2 and (p2 = (p1 = buf) + fread(buf, 1, 100010, stdin), p1 == p2) ? EOF : *p1++;
}
template<class T> void read(T &x) {
    bool flag = 0; x = 0; char ch = Getchar();
    while (!isdigit(ch)) flag |= ch == '-', ch = Getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = Getchar();
    x = flag ? -x : x;
}
struct node {int next, to;}e[A];
int head[A], num;
void add(int fr, int to) {
	e[++num].next = head[fr];
	e[num].to = to;
	head[fr] = num;
}
int n, m, c[A], k, x;
int siz[A], ans, sum[A];
priority_queue<int, vector<int>, greater<int> > q;
void dfs(int fr) {
	for (int i = head[fr]; i; i = e[i].next) {
		int ca = e[i].to;
		dfs(ca);
	}
	q = priority_queue<int, vector<int>, greater<int> > ();
	for (int i = head[fr]; i; i = e[i].next) {
		int ca = e[i].to;
		q.push(sum[ca]);
	}
	while (!q.empty()) {
		int now = q.top(); q.pop();
		if (sum[fr] - 1 + now <= m) {
			sum[fr] += (now - 1);
			ans++;
		}
		else break;
	}
}

int main(int argc, char const *argv[]) {
	read(n); read(m);
	for (int i = 1; i <= n; i++) read(c[i]);
	for (int i = 1; i <= n; i++) {
		read(k);
		siz[i] = k;
		sum[i] = siz[i] + c[i];
		while (k--) {
			read(x);
			add(i, x + 1);
		}
	}
	dfs(1);
	cout << ans << endl;
}

这个题目中需要每次把优先队列清空,由于优先队列没有内置的 c l e a r ( ) clear() clear()函数,所以我们只能自己来实现清空队列。具体可以选择不断弹出元素,也可以选择给优先队列重新赋值。

while (!q.empty()) q.pop();

q = priority_queue<int> ();

你可能感兴趣的:(算法,深度优先,图论,贪心算法)