1. 程式人生 > >BZOJ3252 攻略 [樹鏈剖分]

BZOJ3252 攻略 [樹鏈剖分]

  題目傳送門

攻略

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit: 1169  Solved: 554
[Submit][Status][Discuss]

Description

題目簡述:樹版[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

HINT

 


  分析:

  網上大部分人好像都是用的$dfs$序+線段樹,實際上不用那麼麻煩,直接上樹鏈剖分,加個貪心就行了。

  對於這道題,肯定要優先選取從起點(不一定是根,因為走過的路徑不能重複算,所以有的路徑走過以後會影響其他路徑)到葉子節點權值和最大的路徑,這顯然就是樹鏈剖分的輕重鏈/長短鏈(廣義上)的思想,不過這裡把剖分的標準換成權值和就行了。

  剖分完以後,$top[]$值等於自身的節點就是一條路徑的起點,我們把它的權值和丟進大根堆裡維護然後取前$k$大就行了。

  Code:

 

//It is made by HolseLee on 9th Nov 2018
//BZOJ 3252
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int N=2e5+7;
int n,m,head[N],cnte,fa[N],hson[N],top[N];
ll a[N],siz[N],ans;
struct Edge { int to,nxt; }e[N<<1];
priority_queue<ll>q;

inline ll read()
{
    char ch=getchar(); ll x=0; bool flag=false;
    while( ch<'0' || ch>'9' ) {
        if( ch=='-' ) flag=true; ch=getchar();
    }
    while( ch>='0' && ch<='9' ) {
        x=x*10+ch-'0'; ch=getchar();
    }
    return flag ? -x : x;
}

inline void add(int x,int y)
{
    e[++cnte].to=y; e[cnte].nxt=head[x];
    head[x]=cnte;
}

void dfs1(int x)
{
    siz[x]=a[x];
    for(int i=head[x],y; i; i=e[i].nxt) {
        y=e[i].to;
        if( y==fa[x] ) continue;
        fa[y]=x; dfs1(y);
        siz[x]=max(siz[x],siz[y]+a[x]);
        if( siz[y]>siz[hson[x]] ) hson[x]=y;
    }
}

void dfs2(int x,int nowtop)
{
    top[x]=nowtop;
    if( !hson[x] ) return;
    dfs2(hson[x],nowtop);
    for(int i=head[x],y; i; i=e[i].nxt) {
        y=e[i].to;
        if( y==fa[x] || y==hson[x] ) continue;
        dfs2(y,y);
    }
}

int main()
{
    n=read(); m=read();
    for(int i=1; i<=n; ++i) a[i]=read();
    int x,y;
    for(int i=1; i<n; ++i) {
        x=read(), y=read();
        add(x,y), add(y,x);
    }
    dfs1(1); dfs2(1,1);
    for(int i=1; i<=n; ++i)
    if( top[i]==i ) q.push(siz[i]);
    while( (m--) && (!q.empty()) ) {
        ans+=q.top(); q.pop();
    }
    printf("%lld\n",ans);
    return 0;
}