BZOJ 3252 攻略

题目简述:树版[k取方格数]

众所周知,桂木桂马是攻略之神,开启攻略之神模式后,他可以同时攻略k部游戏。今天他得到了一款新游戏《XX

半岛》,这款游戏有n个场景(scene),某些场景可以通过不同的选择支到达其他场景。所有场景和选择支构成树状

结构:开始游戏时在根节点(共通线),叶子节点为结局。每个场景有一个价值,现在桂马开启攻略之神模式,同

时攻略k次该游戏,问他观赏到的场景的价值和最大是多少(同一场景观看多次是不能重复得到价值的)

“为什么你还没玩就知道每个场景的价值呢?”

“我已经看到结局了。”

Input

第一行两个正整数n,k

第二行n个正整数,表示每个场景的价值

以下n-1行,每行2个整数a,b,表示a场景有个选择支通向b场景(即a是b的父亲)

保证场景1为根节点

n<=200000,1<=场景价值<=2^31-1

Output

输出一个整数表示答案

Sample Input

5 2 4 3 2 1 1 1 2 1 5 2 3 2 4

Sample Output

10

思路: 贪心的去想,就是我们先取出权值最大的一条链,然后逐个更新链上的每个点的子树,更新完之后再取出最大的一条链,那么我们就需要一种快速的取最大值和id的方案,当然线段树,当然要配合dfs序,我们在更新的时候,如果更新到的这个节点已经被更新过了,那么就可以直接跳出了,因为每个节点最多只能被用一次。

思路2: 我们对于每一个节点,我把这个节点的权值给他的若干个孩子中的哪个孩子呢,当然我尽量的想给最大的那个孩子,然后我们可以对于每个一个节点维护一个可并堆,就可以直接把当前节点的权值给权值最大的孩子链,那么一路dfs回退,回退到根节点,堆顶的前k个元素就是答案呗。

代码:

///11111111111111111111111111111111111111111111
#include<bits/stdc++.h>
#define lson (i<<1)
#define rson (i<<1|1)

using namespace std;
typedef long long ll;
const int N =200005;

struct node
{
    int l,r;
    int mxid;
    ll lz;
    ll maxx;
}tr[N<<2];

vector<int >ve[N];
int tim;
int L[N];
int R[N];
int dfns[N];
int dfn;
int f[N];
int vis[N];
ll w[N];
ll val[N];
int n;


void dfs(int u,int fa)
{
    val[u]=val[fa]+w[u];
    f[u]=fa;
    dfns[++dfn]=u; L[u]=dfn;
    for(int i=0;i<ve[u].size();i++){
        int v=ve[u][i];
        if(v==fa) continue;
        dfs(v,u);
    }
    R[u]=dfn;
}

void push_up(int i)
{
    ll &maxx=tr[i].maxx; int &mxid=tr[i].mxid;
    if(tr[lson].maxx>=tr[rson].maxx){
        maxx=tr[lson].maxx; mxid=tr[lson].mxid;
    }
    else{
        maxx=tr[rson].maxx; mxid=tr[rson].mxid;
    }
}

void push_down(int i)
{
    ll &lz=tr[i].lz;
    if(lz){
        tr[lson].maxx-=lz; tr[lson].lz+=lz;
        tr[rson].maxx-=lz; tr[rson].lz+=lz;
        lz=0;
    }
}

void build(int i,int l,int r)
{
    tr[i].l=l; tr[i].r=r; tr[i].lz=0; tr[i].maxx=0; tr[i].mxid=0;
    if(l==r){
        tr[i].maxx=val[dfns[l]];
        tr[i].mxid=dfns[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
    push_up(i);
}

void update(int i,int l,int r,ll lz)
{
    if(tr[i].l==l&&tr[i].r==r){
        tr[i].maxx-=lz;
        tr[i].lz+=lz;
        return ;
    }
    push_down(i);
    int mid=(tr[i].l+tr[i].r)>>1;
    if(r<=mid) update(lson,l,r,lz);
    else if(l>mid) update(rson,l,r,lz);
    else{
        update(lson,l,mid,lz);
        update(rson,mid+1,r,lz);
    }
    push_up(i);
}

int main()
{
    int k;
    scanf("%d %d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
    int u,v;
    for(int i=1;i<n;i++){
        scanf("%d %d",&u,&v);
        ve[u].push_back(v);
        ve[v].push_back(u);
    }
    dfs(1,0);


    build(1,1,n);
    ll ans=0;
    while(k--)
    {
        ans+=tr[1].maxx;
        int id=tr[1].mxid;
        while(1){
            if(id==0) break;
            if(vis[id]) break;
            int l,r;
            l=L[id]; r=R[id];
            update(1,l,r,w[id]);
            vis[id]=1;
            id=f[id];
        }
    }
    printf("%lld\n",ans);

    return 0;
}


/// 222222222222222222222222222222222
#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int N =2e5+5;
vector<int >ve[N];

struct merge_dui
{
    int l,r;
    int dis;
    ll val;
}T[N];
int root[N];
int cnode;

ll w[N];
int n;

int Merge(int r1,int r2)
{
    if(r1==0||r2==0) return r1+r2;
    if(T[r1].val<T[r2].val) swap(r1,r2);
    T[r1].r=Merge(T[r1].r,r2);
    if(T[T[r1].l].dis<T[T[r1].r].dis) swap(T[r1].l,T[r1].r);
    T[r1].dis=T[T[r1].r].dis+1;
    return r1;
}

void dfs(int u,int fa)
{
    if(ve[u].size()==1&&fa!=-1){
        root[u]=++cnode;
        T[root[u]].val=w[u];
        return ;
    }
    root[u]=0;
    T[root[u]].val=0;
    for(int i=0;i<ve[u].size();i++){
        int v=ve[u][i];
        if(v==fa) continue;
        dfs(v,u);
        root[u]=Merge(root[u],root[v]);
    }
    T[root[u]].val+=w[u];
    return ;
}

int Pop(int rt)
{
    return Merge(T[rt].l,T[rt].r);
}

int main()
{
    int k,u,v;
    scanf("%d %d",&n,&k);
    for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
    for(int i=1;i<n;i++){
        scanf("%d %d",&u,&v);
        ve[u].push_back(v);
        ve[v].push_back(u);
    }
    dfs(1,-1);

    ll ans=0;
    while(k--)
    {
        ans+=T[root[1]].val;
        root[1]=Pop(root[1]);
    }
    printf("%lld\n",ans);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值