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 }