BZOJ3252: 攻略

Posted 小时のblog

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ3252: 攻略相关的知识,希望对你有一定的参考价值。

题目大意:

一棵有根的有点权的树。

每次可以取某个叶子结点到根的路径的点权和。

并把取过的清0.可以取k次,求取到的最大权值。

题解:

贪心+dfs序+线段树

明显每次取叶子到根的路径权值和最大的,

把叶子节点到根的权值建在线段树上。

每次把路径上节点清0。

假如把p节点清0,在p子树中的叶子节点的sum都会

减去这个节点的值。

sum为根到这个叶子结点的权值和。

代码:

技术分享
#include<iostream>
#include<cstdio>
#include<cstring>
#define N 200009
#define LL long long
using namespace std;

int n,k;

int sumedge,head[N],b[N];

int cnt,re[N],l[N],r[N],dad[N],vis[N];

LL ans,a[N],w[N];

inline int read(){
    char ch=getchar();int x=0,f=1;
    for(;!isdigit(ch);ch=getchar())if(ch==-)f=-1;
    for(;isdigit(ch);ch=getchar())x=x*10+ch-0;
    return x*f;
}

struct Edge{
    int x,y,nxt;
    Edge(int x=0,int y=0,int nxt=0):
        x(x),y(y),nxt(nxt){}
}edge[N];

struct Tree{
    int l,r,p;
    LL mx,s;
}tr[N<<2];

void add(int x,int y){
    edge[++sumedge]=Edge(x,y,head[x]);
    head[x]=sumedge;
}

void dfs(int x,LL sum){
    bool flag=true;l[x]=cnt+1;
    for(int i=head[x];i;i=edge[i].nxt){
        int v=edge[i].y;
        if(v==dad[x])continue;
        dad[v]=x;w[v]+=w[x];flag=false;
        dfs(v,w[v]);
    }
    if(flag)a[++cnt]=sum,re[cnt]=x;
    r[x]=cnt;
}

void pushup(int rt){
    tr[rt].mx=max(tr[rt<<1].mx,tr[rt<<1|1].mx);
    tr[rt].p=tr[rt<<1].mx>tr[rt<<1|1].mx?tr[rt<<1].p:tr[rt<<1|1].p;
    return;
}

void pushdown(int rt){
    if(tr[rt].s==0)return;
    tr[rt<<1].s+=tr[rt].s;tr[rt<<1|1].s+=tr[rt].s;
    tr[rt<<1].mx-=tr[rt].s;tr[rt<<1|1].mx-=tr[rt].s;
    tr[rt].s=0;
}

void build(int rt,int l,int r){
    tr[rt].l=l;tr[rt].r=r;
    if(l==r){
        tr[rt].mx=a[l];
        tr[rt].p=l;
        return;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);build(rt<<1|1,mid+1,r);
    pushup(rt);
}

void change(int rt,int l,int r,int ql,int qr,int p){
    if(l>=ql&&r<=qr){
        tr[rt].mx-=p;
        tr[rt].s+=p;
        return;
    }
    pushdown(rt);
    int mid=(l+r)>>1;
    if(ql<=mid)change(rt<<1,l,mid,ql,qr,p);
    if(qr>mid)change(rt<<1|1,mid+1,r,ql,qr,p);
    pushup(rt);
}

int main(){
    n=read();k=read();
    for(int i=1;i<=n;i++)w[i]=read(),b[i]=w[i];
    for(int i=1;i<n;i++){
        int x,y;
        x=read();y=read(); 
        add(x,y);                              
    }
    dfs(1,w[1]);                               
    build(1,1,cnt);                             
    for(int i=1;i<=k;i++){
        LL t=tr[1].mx;int p=tr[1].p;          
        if(t<=0)break;ans+=t;                 
        p=re[p];                              
        while(p&&vis[p]==0){                   
            vis[p]=true;                       
            change(1,1,cnt,l[p],r[p],b[p]);    
            p=dad[p];                         
        }
    }
    printf("%lld\n",ans);
    return 0;
}
AC

 

以上是关于BZOJ3252: 攻略的主要内容,如果未能解决你的问题,请参考以下文章

线段树 BZOJ3252 攻略

BZOJ-3252攻略 DFS序 + 线段树 + 贪心

bzoj3252

bzoj3252: 攻略

BZOJ_3252_攻略_线段树+dfs序

BZOJ3252: 攻略