codeforces 980E The Number Games

本文介绍了一种基于树形结构的贪心算法问题解决思路,目标是在删除k个节点后,使剩余节点的2的幂次和最大。通过从大编号节点开始,递归标记并保留尽可能多的大节点来实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:

给出一棵树,要求去掉k个点,使得剩下的还是一棵树,并且要求Σ(2^i)最大,i是剩下的节点的编号。

思路:

要使得剩下的点的2的幂的和最大,那么肯定要保住大的点,这是贪心。

考虑去掉哪些点的话,那么去掉一个点,它相连的子树的点肯定都得去掉,很麻烦。

所以放过来考虑保留哪些点,那么就从大到小考虑是否保留当前的点。并且把保留的点做标记。

首先n这个点是肯定可以保留的,标记,然后依次考虑n-1,n-2。。。1。

对于当前的点,判断是否已经被标记,如果没有被标记,那么就找到离当前点最远的没有被标记的祖先,如果要标记这个点,那么总共增加的点就是从当前点到离当前点最远的没有被标记的祖先的路径上的所有点的数量,也可以解释为当前点到已经保留的点所构成的树的路径上的点。

找这个路径的时候,暴力的话,n^2,肯定会T,所以可以首先用倍增预处理,再来找祖先,复杂度就可以下降为O(nlogn)。

如果这个数量没有超过要求,那么就把路径上的点全部标记。

最后没有标记的点就是要抛弃的点。

代码:

 1 #include <stdio.h>
 2 #include <string.h>
 3 #include <algorithm>
 4 #include <vector>
 5 using namespace std;
 6 const int N = 1e6 + 10;
 7 int n,k;
 8 vector<int> g[N];
 9 int fa[N];
10 int dep[N];
11 bool vis[N];
12 int anc[N][35];
13 void dfs(int u,int f)
14 {
15     fa[u] = f;
16     for (int v : g[u])
17     {
18         if (v != f)
19         {
20             dep[v] = dep[u] + 1;
21             dfs(v,u);
22         }
23     }
24 }
25 void rdfs(int u,int f)
26 {
27     vis[u] = 1;
28     if (u == f) return;
29     rdfs(fa[u],f);
30 }
31 void preprocess()
32 {
33     for (int i = 0;i < n;i++)
34     {
35         anc[i][0] = fa[i];
36         for (int j = 1;(1<<j) < n;j++) anc[i][j] = -1;
37     }
38     for (int j = 1;(1<<j) < n;j++)
39     {
40         for (int i = 0;i < n;i++)
41         {
42             if (anc[i][j-1] != -1)
43             {
44                 int a = anc[i][j-1];
45                 anc[i][j] = anc[a][j-1];
46             }
47         }
48     }
49 }
50 int main()
51 {
52     memset(anc,-1,sizeof(anc));
53     scanf("%d%d",&n,&k);
54     k = n - k;
55     for (int i = 1;i < n;i++)
56     {
57         int x,y;
58         scanf("%d%d",&x,&y);
59         x--,y--;
60         g[x].push_back(y);
61         g[y].push_back(x);
62     }
63     dfs(n-1,-1);
64     preprocess();
65     int num = 1;
66     vis[n-1] = 1;
67     for (int i = n - 2;i >= 0;i--)
68     {
69         if (vis[i]) continue;
70         int t = i;
71         for (int j = 20;j >= 0;j--)//j是从大到小枚举
72         {
73             if (anc[t][j] == -1) continue;
74             else
75             {
76                 if (!vis[anc[t][j]])
77                 {
78                     t = anc[t][j];
79                 }
80             }
81         }
82         if (dep[i] - dep[t] + 1 + num <= k)
83         {
84             num += dep[i] - dep[t] + 1;
85             rdfs(i,t);
86         }
87     }
88     for (int i = 0;i < n;i++)
89     {
90         if (!vis[i]) printf("%d ",i+1);
91     }
92     return 0;
93 }

 

转载于:https://www.cnblogs.com/kickit/p/9015697.html

内容概要:本文深入探讨了Kotlin语言在函数式编程和跨平台开发方面的特性和优势,结合详细的代码案例,展示了Kotlin的核心技巧和应用场景。文章首先介绍了高阶函数和Lambda表达式的使用,解释了它们如何简化集合操作和回调函数处理。接着,详细讲解了Kotlin Multiplatform(KMP)的实现方式,包括共享模块的创建和平台特定模块的配置,展示了如何通过共享业务逻辑代码提高开发效率。最后,文章总结了Kotlin在Android开发、跨平台移动开发、后端开发和Web开发中的应用场景,并展望了其未来发展趋势,指出Kotlin将继续在函数式编程和跨平台开发领域不断完善和发展。; 适合人群:对函数式编程和跨平台开发感兴趣的开发者,尤其是有一定编程基础的Kotlin初学者和中级开发者。; 使用场景及目标:①理解Kotlin中高阶函数和Lambda表达式的使用方法及其在实际开发中的应用场景;②掌握Kotlin Multiplatform的实现方式,能够在多个平台上共享业务逻辑代码,提高开发效率;③了解Kotlin在不同开发领域的应用场景,为选择合适的技术栈提供参考。; 其他说明:本文不仅提供了理论知识,还结合了大量代码案例,帮助读者更好地理解和实践Kotlin的函数式编程特性和跨平台开发能力。建议读者在学习过程中动手实践代码案例,以加深理解和掌握。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值