BZOJ 3252 攻略 线段树

题意:链接

方法:线段树

解析:

闲的随机的题。

看完题后看着好像挺简单的。

既然每个点的权值只会传子树,并且整个图是严格的一棵树,所以应该是跟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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值