E1. Weights Division (easy version)

题目来源

https://codeforces.ml/contest/1399/problem/E1

题意分析

  题目意思的大致可以描述为,给定一棵带权有根树,其根为编号为1的点,要求根节点到所有叶结点的距离之和小于一个题目给出的值S。为了达到目标,每一次我们可以选定一条边,将其权值 wi 除以2向下取整。问最少需要多少次操作。 

思路分析

  一看这个题目,可以意识到的是可以用贪心来解决这个问题。抛开树不讲,我们将所有的边压入一个优先队列,每一次取最大的值来进行一次操作即可。
  那么接下来的任务就是如何从获得这些边的值。可以想到的是,很多的边,其所贡献出的权值并不就是他自身的权值,因为会出现某一个结点下面有好多个叶结点,那么该结点连向父节点的那条边所通过的总次数就不再是1次。这里我们可以使用一个 dfs 去实现,即在dfs中,每次遇到一个结点,在遍历完所有连向他的边的过程中,记录下以他的子节点为根的子树中的叶结点的个数,即最后可以得到以该结点为根的子树中叶结点的个数,并把这个个数赋值给他连向父节点的边即可。
  最后再通过遍历所有的边,完成操作数量的判断。
  题目有些细节需要注意,首先每一次操作,是将边的权值除以2向下取整,而不是将他的贡献值除以2向下取整,这两者有区别(这仿佛是读题的问题)。还有就是优先队列中的排序方式,应该是按照每一次操作贡献度的降低为关键字进行排序,而不能直接以贡献度的大小进行排序。这里给出例子:权值为1的边需要经过70次以及一个权值为10的边需要经过8次。

  (wa了好多次,我真菜。。)

code

 1 #include <iostream>
 2 #include <cstring>
 3 #include <queue>
 4 #include <algorithm>
 5 #include <stack>
 6 #include <set>
 7 #include <map>
 8 
 9 #define int long long
10 using namespace std;
11 const int maxn = 1e5 + 7;
12 struct edge{
13     int u, v, w, nxt, num;
14 };
15 
16 struct node{
17     int w, num;
18     node(){}
19     node(int _w, int _num):w(_w), num(_num){}
20     bool operator<(const node& a) const{
21         return w*num-(w/2)*num<a.w*a.num-(a.w/2)*a.num;
22     }
23 };
24 
25 edge e[maxn * 4];
26 int head[maxn], hcnt, in[maxn];
27 priority_queue <node> q;
28 
29 void init(){
30     memset(in, 0, sizeof (in));
31     memset(head, -1, sizeof (head));
32     hcnt = 0;
33     while (!q.empty()) q.pop();
34 }
35 
36 void adde(int u, int v, int w){
37     e[hcnt].u = u; e[hcnt].v = v; e[hcnt].w = w; e[hcnt].num = 0;
38     e[hcnt].nxt = head[u]; head[u] = hcnt ++;
39 
40     e[hcnt].u = v; e[hcnt].v = u; e[hcnt].w = w; e[hcnt].num = 0;
41     e[hcnt].nxt = head[v]; head[v] = hcnt ++;
42 }
43 
44 void dfs(int x, int f){
45     int ft = -1, nm = 0;
46     for (int i=head[x]; ~i; i=e[i].nxt){
47         int v = e[i].v, w = e[i].w;
48         if (v == f) {ft = i; continue;}
49         dfs(v, x);
50         nm += e[i^1].num;
51     }
52     if (in[x] == 1) nm ++;
53     if (ft != -1)
54         e[ft].num = nm;
55 }
56 
57 
58 
59 signed main(){
60     int t; scanf("%lld", &t);
61     while (t --){
62         init();
63         int n, s;
64         scanf("%lld%lld", &n, &s);
65         int _u, _v, _w;
66         for (int i=1; i<n; i++){
67             scanf("%lld%lld%lld", &_u, &_v, &_w);
68             adde(_u, _v, _w);
69             in[_u] ++; in[_v] ++;
70         }
71 
72         dfs(1, 0);
73         int ans = 0, sum = 0;
74         for (int i=0; i<hcnt; i++) {
75 //            printf("&& %lld  %lld %lld  \n", e[i].num, e[i].w ,e[i].num * e[i].w);
76             sum += e[i].num * e[i].w;
77             q.push(node(e[i].w , e[i].num));
78         }
79 
80         while (sum > s){
81             node h = q.top();
82             sum = sum - h.w * h.num + (h.w / 2) * h.num;
83             q.pop();
84             q.push(node(h.w / 2, h.num));
85             ans ++;
86         }
87 
88         printf("%lld\n", ans);
89     }
90     return 0;
91 }

 

posted @ 2020-08-30 11:59  Rain_island  阅读(152)  评论(0编辑  收藏  举报
Title