题意:链接
方法:线段树
解析:
闲的随机的题。
看完题后看着好像挺简单的。
既然每个点的权值只会传子树,并且整个图是严格的一棵树,所以应该是跟dfs序有关。
然后去看数据范围。
尼玛HINT是什么鬼。
既然这么说了那就想想怎么做吧=-=
并且因为价值都为正的,所以显然要考虑贪心,挑k条从叶节点到根的所有点权值和最大的k条。
并且每一挑完后都需要更新。
然后有一个性质,每个点至多选一次,也就是说每个点至多被删一次。
并且根节点到叶节点链上的所有点的路径上的点权和是随着深度递增的。
所以显然我们用线段树维护一个最大值以及哪个节点取到最大值就好了。
如果当前挑出来的最大值为0了,并且还没有选够k条,直接跳出就好。
然后将这些最大值累加即可。
代码:
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 200010
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
using namespace std;
typedef long long ll;
ll n,k,tot;
ll a[N];
ll head[N],cnt;
ll fa[N],st[N],ed[N],vis[N],adfn[N];
struct node
{
ll from,to,next;
}edge[N];
void init()
{
memset(head,-1,sizeof(head)),cnt=1;
}
void edgeadd(ll from,ll to)
{
edge[cnt].from=from,edge[cnt].to=to,edge[cnt].next=head[from];
head[from]=cnt++;
}
void dfs(ll now,ll ff)
{
st[now]=++tot,fa[now]=ff,adfn[tot]=now;
for(ll i=head[now];i!=-1;i=edge[i].next)
{
ll to=edge[i].to;
dfs(to,now);
}
ed[now]=tot;
}
ll ma[N<<2],col[N<<2],no[N<<2];
void pushup(ll rt)
{
if(ma[rt<<1]>ma[rt<<1|1])
{
ma[rt]=ma[rt<<1];
no[rt]=no[rt<<1];
}else
{
ma[rt]=ma[rt<<1|1];
no[rt]=no[rt<<1|1];
}
}
void pushdown(ll rt)
{
if(col[rt]!=0)
{
ma[rt<<1]+=col[rt];
ma[rt<<1|1]+=col[rt];
col[rt<<1]+=col[rt];
col[rt<<1|1]+=col[rt];
col[rt]=0;
}
}
void build(ll l,ll r,ll rt)
{
if(l==r)
{
no[rt]=l;
return;
}
ll mid=(l+r)>>1;
build(lson);
build(rson);
}
void update(ll L,ll R,ll l,ll r,ll rt,ll v)
{
if(L<=l&&r<=R)
{
ma[rt]+=v;
col[rt]+=v;
return;
}
pushdown(rt);
ll mid=(l+r)>>1;
if(L<=mid)update(L,R,lson,v);
if(R>mid)update(L,R,rson,v);
pushup(rt);
}
int main()
{
init();
scanf("%lld%lld",&n,&k);
for(ll i=1;i<=n;i++)scanf("%lld",&a[i]);
for(ll i=1;i<n;i++)
{
ll x,y;
scanf("%lld%lld",&x,&y);
edgeadd(x,y);
}
dfs(1,0);
build(1,n,1);
for(ll i=1;i<=n;i++)
{
update(st[i],ed[i],1,n,1,a[i]);
}
ll ans=0;
while(k--)
{
if(ma[1]==0)break;
ll u=adfn[no[1]];
ans+=ma[1];
while(u!=0&&!vis[u])
{
update(st[u],ed[u],1,n,1,-a[u]);
vis[u]=1,u=fa[u];
}
}
printf("%lld\n",ans);
}
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-06 03:18:12