CF77C Beavermuncher-0xFF

题意简述

题目链接

  给定一棵树,每个节点有一个权值k,表示该节点有多少个海狸,从根节点出发,每吃一个海狸便能够且必须跳到与当前节点有直接边相连的节点上,要求最终跳回根节点,求最多能吃多少个海狸。

算法概述

  考虑每个节点产生的贡献。

  首先明确一点:每个节点产生的贡献与且只与其儿子节点有关。

  先dfs递归计算出每个儿子的贡献。然后考虑当前节点,若当前节点u不是树根,则先将k[u]减去1(因为还要跳回父亲,需要吃掉1个海狸)。

  设sons[u]为节点u的儿子数,计算当前节点的贡献:

  (i) 若k[u]<sons[u],即无法吃遍所有儿子,则将儿子的贡献值排序,从大到小吃,每吃一个儿子将k[u]减1,直到k[u]减为0结束。

  (ii) 若k[u]>=sons[u],说明可以吃遍所有儿子,那么进行完(i)中的操作(即吃遍所有儿子)之后k[u]还有剩余,记为last,则考虑在u与其儿子节点之间来回跳跃,sum统计所有儿子的剩余权值之和,那么还可产生的贡献即为2*min(last,sum)。

  时间复杂度分析:

  不难发现,时间复杂度的瓶颈主要在对所有儿子的贡献值进行排序的操作上。

  设每个节点的儿子数量为s,则总时间=s1logs1+s2logs2+……+snlogsn<=s1logn+s2logn+……+snlogn=(s1+s2+……+sn)logn=(n-1)logn。

  故时间复杂度为O(nlogn)

参考代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>

#define x first
#define y second

using namespace std;

typedef long long ll;
typedef pair<ll,ll> pll; //从该点出发往下跳的贡献值,剩余权值 
const int N=1e5+10;

struct Edge{
	int to,next;
}edge[N<<1];int idx;
int h[N];

int k[N];
int n,root;

void add_edge(int u,int v){edge[++idx]={v,h[u]};h[u]=idx;}

pll dfs(int p,int fa)
{
	vector<ll> v; //所有儿子的贡献放入vector中 
	ll sum=0;
	int flag=1; //是否为叶子节点 
	for(int i=h[p];~i;i=edge[i].next)
	{
		int to=edge[i].to;
		if(to==fa)continue;
		flag=0;
		pll s=dfs(to,p);
		sum+=s.y;
		v.push_back(s.x);
	}
	if(flag)return make_pair(0,k[p]-1); 
	/*
	由于第一维是统计从该点出发往下跳,最后跳回来的总贡献值,
	而该点为叶子节点,故为0。
	第二维即从该点跳回父亲之后,剩余的权值,
	由于跳回父亲需要吃掉一个海狸
	故为k[p]-1。
	而其跳回父亲这一步的贡献会在其父亲节点处计算。 
	*/ 
	
	sort(v.begin(),v.end());
	
	ll last=k[p]-(p==root?0:1),eat=0; //若为根,则不必跳回父亲,否则需要跳回父亲,故减1。 
	for(int i=v.size()-1;i>=0&&last;i--,last--)eat+=v[i]+2; 
	//v[i]为以该儿子为根的子树的总贡献,而后需要加上从该点跳到该儿子的贡献1以及从该儿子跳回该点的贡献1,故加2。 
	eat+=2*min(last,sum); //来回跳跃,一来一回即产生了2的贡献,故而是两倍的跳跃步数。 
	last-=min(last,sum); //维护剩余权值,来回跳跃之后需要减去跳跃步数。 
	//此处上面两行不必考虑last是否已经减为0,因为若last为0的话,则即使执行上面两行,也不会对答案产生影响。 
	return make_pair(eat,last);
}

int main()
{
	memset(h,-1,sizeof h);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%d",&k[i]);
	
	for(int i=1;i<=n-1;i++)
	{
		int u,v;scanf("%d%d",&u,&v);
		add_edge(u,v),add_edge(v,u); 
	}
	
	scanf("%d",&root);
	
	printf("%lld\n",dfs(root,0).x);
	
	return 0;
}

  

posted @ 2020-08-03 17:04  魑吻丶殇之玖梦  阅读(167)  评论(0编辑  收藏  举报