BZOJ3252 攻略 贪心、长链剖分

时间:2023-03-08 17:46:46

传送门


给树竟直接给父子关系!!!真良心

首先一个贪心策略:每一次选择的链一定是所有链中权值最大的。这应该比较显然

那么我们接下来考虑如何维护这个贪心。我们可以使用长链剖分进行维护,对权值进行长链剖分,然后取前$K$大的链权值和,就是答案了。

考虑这个贪心为什么是对的。假设我们选到了第$i$条链,意味着第$i$条链的链顶的所有祖先都一定已经被访问过了(否则它一定是其父亲的儿子中链最长的点,它就不会是链顶),所以选择这一条链的意义就是选择从根到这一条链的链底的路径。

 #include<bits/stdc++.h>
 #define int long long
 //This code is written by Itst
 using namespace std;

 inline int read(){
     ;
     ;
     char c = getchar();
     while(c != EOF && !isdigit(c)){
         if(c == '-')
             f = ;
         c = getchar();
     }
     while(c != EOF && isdigit(c)){
         a = (a << ) + (a << ) + (c ^ ');
         c = getchar();
     }
     return f ? -a : a;
 }

 ;
 struct Edge{
     int end , upEd;
 }Ed[MAXN << ];
 int head[MAXN] , len[MAXN] , maxLen[MAXN] , son[MAXN] , ans[MAXN] , val[MAXN] , N , K , cnt , cntEd;

 inline void addEd(int a , int b){
     Ed[++cntEd].end = b;
     Ed[cntEd].upEd = head[a];
     head[a] = cntEd;
 }

 void dfs1(int x , int p){
     maxLen[x] = len[x] = val[x] + len[p];
     for(int i = head[x] ; i ; i = Ed[i].upEd){
         dfs1(Ed[i].end , x);
         if(maxLen[Ed[i].end] > maxLen[x]){
             maxLen[x] = maxLen[Ed[i].end];
             son[x] = Ed[i].end;
         }
     }
 }

 void dfs2(int x , int t){
     if(t == x)
         ans[++cnt] = maxLen[x] - len[x] + val[x];
     for(int i = head[x] ; i ; i = Ed[i].upEd)
         dfs2(Ed[i].end , Ed[i].end == son[x] ? t : Ed[i].end);
 }

 bool cmp(int a , int b){
     return a > b;
 }

 signed main(){
 #ifndef ONLINE_JUDGE
     freopen("3252.in" , "r" , stdin);
     //freopen("3252.out" , "w" , stdout);
 #endif
     N = read();
     K = read();
      ; i <= N ; ++i)
         val[i] = read();
      ; i < N ; ++i){
         int a = read() , b = read();
         addEd(a , b);
     }
     dfs1( , );
     dfs2( , );
     sort(ans +  , ans + cnt +  , cmp);
     ;
      ; i <= K ; ++i)
         sum += ans[i];
     cout << sum;
     ;
 }