HDU 5242 利用树链剖分思想进行贪心

题目大意:

在给定带权值节点的树上从1开始不回头走到某个底端点后得到所有经过的点的权值后,这些点权值修改为0,到达底部后重新回到1,继续走,问走k次,最多能得到多少权值之和

这其实就是相当于每一次都走权值最大的那一条路径,进行贪心k次

首先先来想想树链剖分的时候的思想:

重儿子表示这个儿子对应的子树的节点数最多,那么每次访问都优先访问重儿子

这道题里面我们进行一下转化,如果当前儿子能走出一条最长的路径,我们就令其为重儿子,那么很容易想到,到达父亲时,如果选择重儿子,那么之前到达

父亲所得的权值一定是记录在重儿子这条路径上的,那么访问轻儿子的时候,因为前面的值在到达重儿子后修改为0,所以走到轻儿子之前权值和修改为0

我们将所有到达底端点的路径长度保存到rec数组中,将rec排序取前k个即可,如果不够取,相当于全部取完,因为后面再走也就是相当于0,不必计算

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstring>
 4 #include <algorithm>
 5 #include <queue>
 6 using namespace std;
 7 #define N 100100
 8 #define ll long long
 9
10 int first[N] , k  , t;
11  //t记录底层节点的个数,rec[i]记录到达i节点的那个时候经过的长度
12 ll rec[N];
13
14 struct Edge{
15     int y , next;
16     Edge(int y=0 , int next=0):y(y),next(next){}
17 }e[N];
18
19 void add_edge(int x , int y)
20 {
21     e[k] = Edge(y , first[x]);
22     first[x] = k++;
23 }
24
25 bool cmp(ll a , ll b)
26 {
27     return a>b;
28 }
29
30 ll val[N]  , down[N];//down[i]记录从i开始往下能走到的最长的路径
31 int heavyson[N];
32 void dfs(int u)
33 {
34     ll maxn = -1;
35     for(int i=first[u] ; ~i ; i=e[i].next)
36     {
37         int v = e[i].y;
38         dfs(v);
39         if(maxn<down[v]){
40             heavyson[u] = v;
41             maxn = down[v];
42         }
43     }
44     if(maxn>=0) down[u] = maxn+val[u];
45     else down[u] = val[u];
46 }
47
48 void dfs1(int u , ll cur)
49 {
50     bool flag = true; //判断是否为底层节点
51     if(heavyson[u]){
52         dfs1(heavyson[u] , cur+val[heavyson[u]]);
53         flag = false;
54     }
55     for(int i=first[u] ; ~i ; i=e[i].next)
56     {
57         int v = e[i].y;
58         if(v == heavyson[u]) continue;
59         dfs1(v , val[v]);
60         flag = false;
61     }
62     if(flag) rec[t++] = cur;
63 }
64
65 int main()
66 {
67     #ifndef ONLINE_JUDGE
68         freopen("a.in" , "r" , stdin);
69     #endif
70     int T , cas=0;
71     scanf("%d" , &T);
72     while(T--)
73     {
74         printf("Case #%d: " , ++cas);
75         int n,m;
76         scanf("%d%d" , &n , &m);
77         memset(first , -1 , sizeof(first));
78         k=0;
79         for(int i=1 ; i<=n ; i++) scanf("%I64d" , val+i);
80         for(int i=1 ; i<n ; i++){
81             int u,v;
82             scanf("%d%d" , &u , &v);
83             add_edge(u , v);
84         }
85         memset(heavyson , 0 , sizeof(heavyson));
86         dfs(1);
87         t=0;
88         dfs1(1 , val[1]);
89         sort(rec , rec+t , cmp);
90         ll ret = 0;
91         for(int i=0 ; i<t ; i++){
92             if(i==m) break;
93             ret+=rec[i];
94         }
95         printf("%I64d\n" , ret);
96     }
97     return 0;
98 }
时间: 2024-08-17 12:44:47

HDU 5242 利用树链剖分思想进行贪心的相关文章

hdu 5458 Stability(树链剖分+并查集)

Stability Time Limit: 3000/2000 MS (Java/Others)    Memory Limit: 65535/102400 K (Java/Others)Total Submission(s): 1347    Accepted Submission(s): 319 Problem Description Given an undirected connected graph G with n nodes and m edges, with possibly r

HDU 3966 RE 树链剖分 Aragorn&#39;s Story

给一棵点带权的图 有这样一个操作: 使树上某一条路径所有点权值增减 每次询问某个点现在的权值. 树链剖分完以后,就是线段树的成段更新了. 这题感觉A不了了,无限RE,手动开栈也没卵用. 还是把我辛辛苦苦写的代码贴一下吧. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <vector> 6 using

HDU 5044 (树链剖分+树状数组+点/边改查)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5044 题目大意:修改链上点,修改链上的边.查询所有点,查询所有边. 解题思路: 2014上海网赛的变态树链剖分模板题.将以往树链剖分的点&边修改和查询合在一起之后,难度上去不少. 第一个卡人点是读入优化. 第二个卡人点是树状数组.由于要查询所有点,如果使用线段树,每次都要扫到底层才能取出点值,必T无疑. 然后使用树状数组之后,树链剖分的点/边修改写法有些变动. 点查询变化不大. 边查询只要查询一下

HDU 3966(树链剖分+点修改+点查询)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3966 题目大意:营地的分布成树型.每个营地都有一些人,每次修改修改一条链上的所有营地的人数,每次查询单个点. 解题思路: 树链剖分基础题. 维护一个sum. 注意轻链修改时,点修改和边修改的不同. 由于树的结构与线段树点的顺序不太相同,因此需要做一个映射数组rank.故在线段树Build的时候,权值是camp[rank[l]],rank这步的映射在dfs2的时候完成,rank[w[u]]=u; Qu

HDU 5458 Stability (树链剖分+并查集+set)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5458 给你n个点,m条边,q个操作,操作1是删边,操作2是问u到v之间的割边有多少条. 这题要倒着做才容易,倒着加边. 因为这题最后保证所有的点一定连通,所以可以构建一棵树,树链剖分一下.要是u到v之间只有一条边,那便是一条割边.而加边的话,就是将u到v之间的边权都+1,边权等于1的话是桥,大于1的话就不是了.所以我们初始化树的时候,可以将边权初始化为1,加边操作将边权赋值为0.求u到v的割边个数的

HDU 5044 Tree 树链剖分

一棵树,初始边权和点权都为0 现在有m个操作,每一个操作: ADD1 u v k: for nodes on the path from u to v, the value of these nodes increase by k. ADD2 u v k: for edges on the path from u to v, the value of these edges increase by k. 操作完后,输出每一个点的点权和每一条边的边权(边权按照输入顺序输出) 我们把边权也当做点权处

hdu 5242——Game——————【树链剖分思想】

Game Time Limit:1500MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit Status Practice HDU 5242 Appoint description:  System Crawler  (2015-05-26) Description It is well known that Keima Katsuragi is The Capturing God because of his

HDU 3966 基础树链剖分

题意:给一棵树,并给定各个点权的值,然后有3种操作:I C1 C2 K: 把C1与C2的路径上的所有点权值加上KD C1 C2 K:把C1与C2的路径上的所有点权值减去KQ C:查询节点编号为C的权值 1 #pragma comment(linker, "/STACK:1024000000,1024000000") 2 #include <iostream> 3 #include <cstdio> 4 #include <cstring> 5 #in

UVA 11354 - Bond(树链剖分)

UVA 11354 - Bond 题目链接 题意:给定一个图,要求每次询问两点,求出这两点间路径最大危险系数最小 思路:先求最小生成树,在生成树上每次询问求LCT就可以了,利用树链剖分求解 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; #define lson(x) ((x<<1)+1) #d