【bzoj 3252】攻略

题意

我们想到一个贪心,就是每次找到根路径前缀和最大的一个点,取走这条路径,同时把这条路径上的点权变成\(0\)

正确性显然

进一步发现我们需要从树上选择\(m\)条链使得链的总和最大

于是我们考虑换上长链剖分,长儿子定义为往下走点权最大的儿子,每次把最长的路径取走就好了

来一个堆维护一下就好了

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define re register
#define LL long long
#define mp std::make_pair
typedef std::pair<LL,int> pii;
const int maxn=200006;
inline int read() {
	char c=getchar();int x=0;while(c<'0'||x>'9') c=getchar();
	while(c>='0'&&c<='9') x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
std::priority_queue<pii> q;
struct E{int v,nxt;}e[maxn];
int head[maxn],a[maxn],son[maxn],vis[maxn];LL len[maxn];
int n,m,num;
inline void add(int x,int y) {
	e[++num].v=y;e[num].nxt=head[x];head[x]=num;
}
void dfs1(int x) {
	for(re int i=head[x];i;i=e[i].nxt) {
		dfs1(e[i].v);
		if(len[e[i].v]>len[son[x]]) son[x]=e[i].v;
	}
	len[x]=len[son[x]]+a[x];
}
int main() {
	n=read(),m=read();
	for(re int i=1;i<=n;i++) a[i]=read();
	for(re int x,y,i=1;i<n;i++) {
		x=read(),y=read(),add(x,y);
	}
	dfs1(1);
	for(re int i=1;i<=n;i++) q.push(mp(len[i],i));
	int tot=0;LL ans=0;
	while(tot<m) {
		int k=q.top().second;q.pop();
		if(vis[k]) continue;
		ans+=len[k];tot++;
		while(k&&!vis[k]) vis[k]=1,k=son[k];
	}
	printf("%lld\n",ans);
	return 0;
}
posted @ 2019-04-09 19:55  asuldb  阅读(163)  评论(0编辑  收藏  举报