HDU5242 Game(树上贪心)

题目链接 Game

题目的意思很简单, 就是要找一棵树权值最大等等前K条链。

在本题中,走的次数等于min(叶子结点个数,k)

tree[i].sum意为从i号结点出发走到某个叶子结点能得到的最大总价值。

pson[i]表示i号结点若要获得最大价值那么下一步该怎么走。

显然tree[i].sum和pson[i]是从i的各个儿子转移得到的。

那么先做一遍DFS计算出tree[i].sum, 再排序。

然后贪心,从价值最大的那个结点开始选,从大到小。

选的时候,要把他中途经过的结点全部屏蔽(就是说被屏蔽的结点接下来不能被选了)。

当选的次数到达k或者没有结点可选时那就停止。

具体可以看我的代码。

 1 #include <bits/stdc++.h>
 2
 3 using namespace std;
 4
 5 #define REP(i,n)                for(int i(0); i <  (n); ++i)
 6 #define rep(i,a,b)              for(int i(a); i <= (b); ++i)
 7 #define dec(i,a,b)              for(int i(a); i >= (b); --i)
 8 #define for_edge(i,x)           for(int i = H[x]; i; i = X[i])
 9
10 #define LL      long long
11
12 const int N     =    100000      +       10;
13 const int M     =    10000       +       10;
14 const int A     =    30          +       1;
15
16 struct Node{
17     LL sum; int id;
18     friend bool operator < (const Node &a, Node &b){
19         return a.sum > b.sum;
20     }
21 } tree[N];
22
23 int E[N << 2], X[N << 2], H[N << 2], pson[N];
24 bool isleaf[N], v[N], vc[N];
25 LL a[N], c[N], ans;
26 int T, n, k, et, x, y, num;
27 int Case = 0;
28 int cnt = 0;
29
30 bool cmp(LL a, LL b){ return a > b;}
31
32 inline void addedge(int a, int b){
33     E[++et] = b, X[et] = H[a], H[a] = et;
34 }
35
36 void dfs(int x, int fa){
37     LL cnt = 0, tot = 0, w = 0;
38     tree[x].sum = a[x];
39     for_edge(i, x) if (E[i] != fa){
40         int u = E[i]; ++cnt;
41         dfs(u, x);
42         if (tree[u].sum > tot){ tot = tree[u].sum; w = u;}
43     }
44     pson[x] = w;
45     if (cnt == 0)  isleaf[x] = true;
46     else tree[x].sum += tree[w].sum;
47 }
48
49 void tag(int x){
50     v[x] = false;
51     if (!isleaf[x]) tag(pson[x]);
52 }
53
54 int main(){
55
56     scanf("%d", &T);
57     while (T--){
58         et = 0; num = 0;
59         scanf("%d%d", &n, &k);
60         memset(H, 0, sizeof H);
61         memset(c, 0, sizeof c);
62         memset(pson, 0, sizeof pson);
63         memset(vc, false, sizeof vc);
64         memset(isleaf, false, sizeof isleaf);
65         rep(i, 1, n) scanf("%lld", a + i);
66         rep(i, 1, n - 1){
67             scanf("%d%d", &x, &y);
68             addedge(x, y); addedge(y, x);
69             vc[y] = true;
70         }
71         rep(i, 1, n) tree[i].id = i;
72         dfs(1, 0);
73         sort(tree + 1, tree + n + 1);
74         memset(v, true, sizeof v);
75         cnt = 0; ans = 0;
76         rep(i, 1, n) if (v[tree[i].id] && cnt < k){
77             ans += tree[i].sum;
78             tag(tree[i].id);
79             ++cnt;
80         }
81
82         printf("Case #%d: %lld\n", ++Case, ans);
83
84     }
85
86     return 0;
87
88 }
时间: 2024-10-10 18:31:39

HDU5242 Game(树上贪心)的相关文章

bzoj 2525: [Poi2011]Dynamite【二分+树上贪心】

一眼二分.然后重点是树上贪心部分 长得像dp一样,设mn为子树内已炸点的最浅点,mx为子树内没有炸并且需要炸的最深点,然后转移直接从子树继承即可 然后是判断当前u点是否需要炸,当mx[u]+mn[u]<=mid,当前子树可以自己消化,所以mx[u]=-inf:否则,就需要在u炸一下 #include<iostream> #include<cstdio> using namespace std; const int N=300005; int n,m,h[N],cnt,d[N]

HDU 5242 Game (树上贪心|类 树链剖分)

大意:有向图给定n-1条边然后给出每个点的权值,每个点的权值只能累加一次,问从根节点走,可以走k次能累计的最大点权和. 思路:本题可以进行两遍dfs操作,第一次为从叶子节点到跟节点的节点权值和.然后根据权值排序,然后根据排序后的下标,来进行第二遍dfs搜索即从当前到根节点的和. 输出前k大值即可. #include <iostream> #include<cstring> #include<stdio.h> #define inf 0x3f3f3f3f #include

light oj 1219 树上贪心

1 #include <iostream> 2 #include <cstdlib> 3 #include <cstring> 4 #include <queue> 5 #include <cstdio> 6 #include <algorithm> 7 #include <map> 8 //#include <time.h> 9 //#include <ext/pb_ds/assoc_container

【BZOJ2067】[Poi2004]SZN 二分+树上贪心

[BZOJ2067][Poi2004]SZN Description String-Toys joint-stock 公司需要你帮他们解决一个问题. 他们想制造一个没有环的连通图模型. 每个图都是由一些顶点和特定数量的边构成. 每个顶点都可以连向许多的其他顶点.一个图是连通且无环的. 图是由许多的线做成的.一条线是一条连接图中两个顶点之间的路径.由于一些技术原因,两条线之间不能有重叠的部分,要保证图中任意一条边都被且仅被一条线所覆盖.由于一些技术原因,做一个这样的图的模型的费用取决于用了多少条线

[SCOI2016]美味 贪心+主席树

挺水的一道题. 题面传送门 题目大意:每个询问给出b,x,l,r,求[l~r]区间内b xor (x+a[i])    (l<=i<=r) 的最大值. 秒想到trie树上贪心? 好像还有加法啊,直接套可持久化trie树行不通,怎么玩呢. 假设目前处理到第j位,b转成二进制后第j位为1来考虑.设我们目前找到的数是ans,那么,如果有一个i在[l~r]内,且ans-x<=a[i]<=ans+(1<<j)-1-x,那么(a[i]+x)^b在第j位必定是1.如果第j位为0,同理

【BZOJ】1596: [Usaco2008 Jan]电话网络

[算法]树上贪心 [题解] 因为一个点必须被覆盖,那么它如果没有被子树节点覆盖的话,就覆盖它的父节点. 从叶子开始贪心. 注意,如果它自己已经被选了就不需要选父节点了. #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace std; const int maxn=10010; struct edge{int v,from;}e[maxn*

【BZOJ】【4027】【HEOI2015】兔子与樱花

贪心 树上贪心问题……跟APIO2015练习赛的C很像啊…… 我的思路是:从叶子向上考虑,令a[x]表示x这个节点上樱花数量与儿子个数的和(即对于任意的x,都有$a[x]\leq m$)每次从儿子的a值中贪心地选最小的加到当前节点中(当然还要-1),然后就不用管了……因为如果某个儿子不能删去,将后代并入父亲,那么之后也不可能在将父亲删去后,再将这个儿子删去,因为越向上樱花累积的越多,而且这样删过以后儿子也是会加上去的……呃总之就是有这么个性质吧...描述的不是很清楚请见谅. 所以就可以贪心了……

51nod 1273 旅行计划——思维题

某个国家有N个城市,编号0 至 N-1,他们之间用N - 1条道路连接,道路是双向行驶的,沿着道路你可以到达任何一个城市.你有一个旅行计划,这个计划是从编号K的城市出发,每天到达一个你没有去过的城市,并且旅途中经过的没有去过的城市尽可能的多(如果有2条路线,经过的没有去过的城市同样多,优先考虑编号最小的城市),直到所有城市都观光过一遍.现在给出城市之间的交通图T,以及出发地点K,你来设计一个旅行计划,满足上面的条件.例如: (K = 2) 第1天 从2到0 (城市 1 和 0 变成去过的) 第2

HDU 5536 字典树

题意:就是公式. 这现场赛O(n^3)能过,觉得太没天理了. 做法:字典树,枚举两个数,然后在字典树上贪心的跑. #include <bits/stdc++.h> using namespace std; const int MAXN = 100010; struct Trie { int ch[2],size; }T[MAXN]; int root = 1,tot = 1; void Insert(int x) { int o = root; T[o].size++; for(int k =