树上前k大的包含不重复结点的长链
一棵树,不一定是二叉树,在每个结点最多只属于一条链的情况下,处理出其中最长的前k个的长度。
最近训练赛做到两道题了,有必要总结一下。
不过我不知道是否有更专门的叫法。
借鉴了这位大佬的博客:https://www.cnblogs.com/Aragaki/p/11754534.html
例题1.
2019-2020 ACM-ICPC Brazil Subregional Programming Contest
https://codeforces.com/gym/102346/problem/D
题意就是你要抓一个组织的人,这些组织每个人只知道他直接的一个领导的信息,知道一个人,可以依次向上抓他的领导,给你k次机会,求出能抓住的最多的人数。
显然是一棵树,贪心的找法就是每次找目前来说最长的链,且要考虑结点重复的情况,就需要我们处理出前k大的含不重复结点的长链的长度。
算法就是邻接表存这棵树。不知道为啥我第二题比赛时用前向星存树TLE了,换邻接表就成了。
ans数组记录当前节点子树里的最长链长为多少 dfs到一个节点 就把除了最长链上的儿子的ans全部push到q里,qq里存的就是除了最长链以外,当前结点作为根节点的子节点所组成链的长度。
我们将1号结点作为整棵树的根节点,最后把ans[1],ans[1]就是最长链的长度, push到q里 取最大的k个即可
为什么这么做是正确的 因为优先队列q里存的是每个节点的父亲节点去掉最长链后自己当根节点时子树的最长链长度
#include<bits/stdc++.h>
#define debug(x) cout << #x << ": " << x << endl
typedef long long ll;
using namespace std;
const int MAXN=1e5+;
vector<int> g[MAXN];
int ans[MAXN];
ll a[MAXN];
priority_queue<int> q;
void dfs(int x)
{
priority_queue<int> qq;
ans[x] = ;
for (int v : g[x])
{
dfs(v);
qq.push(ans[v]);
}
if (g[x].size())
{
ans[x] = qq.top() + ;
qq.pop();
while (qq.size())
{
q.push(qq.top());
qq.pop();
}
}
}
int main()
{
int n, k, x;
scanf("%d%d", &n, &k);
for (int i = ; i <= n; i++)
{
scanf("%d", &x);
g[x].push_back(i);
}
ll anser = ;
dfs();
q.push(ans[]);//ans[1]为最长链长度
int sz=q.size();
for(int i=;i<=min(k,sz);++i)
{
int tmp=q.top();
anser += tmp;
q.pop();
}
printf("%I64d\n", anser);
return ;
}
第二题
这个好像没地方补。。。
BAPC 2019 The 2019 Benelux Algorithm Programming Contest
A Appeal to the Audience
题意就是分配k个选手到k个叶子节点,每个选手有能力值,层层battle,观众获得的快乐值就是子节点上几个选手能力值的和,然后晋级的是选手能力值中最大的,成为父节点,求出最终最大的得分,题目保证这棵树正好有k个叶子节点。
理解之后发现就是每个选手都可以有贡献,我们肯定要让优秀的选手从更底层上来,对总分产生更大的贡献。
那么就和上面一题一样,我们需要处理出来k个包含不重复节点的最长链,为什么不重复呢?
因为选手相遇了只能晋级一个,只能让一个对接下来的深度产生贡献。
之后对选手能力值排序,贪心的最大的选手,匹配最深的即可。
注意到,最长链的长度需要-1,因为最长链包含根节点,手玩一下样例就可以知道,包含根节点长度为4的链产生3层贡献,而其他的链都是不包括根节点的,直接乘能力值即可。
同样邻接表存树,1号节点为根节点,由于题给为0号节点开始,每次++编号即可。
#include<bits/stdc++.h>
#define debug(x) cout << #x << ": " << x << endl
typedef long long ll;
using namespace std;
const int MAXN=1e5+;
vector<int> g[MAXN];
int ans[MAXN];
ll a[MAXN];
priority_queue<int> q;
void dfs(int x)
{
priority_queue<int> qq;
ans[x] = ;
for (int v : g[x])
{
dfs(v);
qq.push(ans[v]);
}
if (g[x].size())
{
ans[x] = qq.top() + ;
qq.pop();
while (qq.size())
{
q.push(qq.top());
qq.pop();
}
}
}
int main()
{
int n, k, x;
scanf("%d%d", &n, &k);
for(int i=; i<=k; ++i) scanf("%I64d",&a[i]);
for (int i = ; i <= n; i++)
{
scanf("%d", &x);
x++;
g[x].push_back(i);
}
ll anser = ;
dfs();
q.push(ans[]);//ans[1]为最长链长度
sort(a+,a++k);
int t=k;
//int sz=q.size();
for(int i=;i<=k;++i)
{
//debug(q.top());
int tmp=q.top();
if(i==) tmp--;
anser += (a[t--]*tmp);
q.pop();
}
printf("%I64d\n", anser);
return ;
}
然后注意到,这里的q.size()就是叶子节点的数量,与k是一样的,第二题就是覆盖全树,全取完。
树上前k大的包含不重复结点的长链的更多相关文章
- poj2104 主席树 区间K大 在线 无修改
关于主席树: 主席树(Chairman Tree)是一种离线数据结构,使用函数式线段树维护每一时刻离散之后的数字出现的次数,由于各历史版本的线段树结构一致,可以相减得出区间信息,即该区间内出现的数字和 ...
- poj2104 划分树 区间K大 在线 无修改
博主sbit....对于高级数据结构深感无力,然后这些东西在OI竟然烂大街了,不搞就整个人都不好了呢. 于是我勇猛的跳进了这个大坑 ——sbit 区间K大的裸题,在线,无修改. 可以用归并树(\(O( ...
- Permutation UVA - 11525(值域树状数组,树状数组区间第k大(离线),log方,log)(值域线段树第k大)
Permutation UVA - 11525 看康托展开 题目给出的式子(n=s[1]*(k-1)!+s[2]*(k-2)!+...+s[k]*0!)非常像逆康托展开(将n个数的所有排列按字典序排序 ...
- Codeforces Gym101505G:Orchard Division(扫描线+线段树第k大)
题目链接 题意 给出一个m*m的地图,上面有n个点,现在需要用一个自定义面积的矩形笼罩住恰好n/2个点,并且这个矩形需要有一个点在至少一个角落上,问这个矩形最小的面积是多少. 思路 有点类似于扫描线. ...
- hdu 5102 树上前k短路径长度和
http://acm.hdu.edu.cn/showproblem.php?pid=5102 给一棵树,求出所有节点的距离中前k小的路径长度和 由于路径长度的定义为两点之间的边的个数,所有遍历1~n- ...
- FZU 2237 中位数 主席树 树上k大
#include <cstdio> #include <cstring> #include <queue> #include <set> #includ ...
- POJ2104-- K-th Number(主席树静态区间第k大)
[转载]一篇还算可以的文章,关于可持久化线段树http://finaltheory.info/?p=249 无修改的区间第K大 我们先考虑简化的问题:我们要询问整个区间内的第K大.这样我们对值域建线段 ...
- 【大杀器】利用划分树秒杀区间内第k大的数
最近看了一道题,大概就是给出一个序列,不断询问其子区间内第k大的数,下面是个截图 绕了一圈没找到中文版题目,if(你是大佬) then 去看截图:else{我来解释:给出一个整数n,和一个整数m,分别 ...
- 静态区间第k大(主席树)
POJ 2104为例(主席树入门题) 思想: 可持久化线段树,也叫作函数式线段树,也叫主席树(高大上). 可持久化数据结构(Persistent data structure):利用函数式编程的思想使 ...
随机推荐
- djanao请求生命周期
djanao请求生命周期 浏览器发送请求到服务端 服务端的wsgi服务器接收到来自浏览器的请求, 对request做一些预处理, 把浏览器的请求信息(请求方式, 请求头, socket信息等)都封装在 ...
- mac 下面 vim 编辑器 开启语法高亮
cp /usr/share/vim/vimrc ~/.vimrc 拷贝默认的配置文件 vim ~/.vimrc 编辑该文件 在文件的最后加入 syntax on 保存退出即可
- HTTPS工作流程(入门)
1.CA(为服务器做担保的第三方机构)将包含CA[公钥C]等信息的[证书C]发送给浏览器: 2.服务器将其[公钥S]和网站信息发送给CA: 3.CA用CA[私钥C]将这些信息加密得到了签名后的[服务器 ...
- Vue + TypeScript 踩坑总结
vue 和 TypeScript 结合的情况下,很多写法和我们平时的写法都不太一样,这里总结我项目开发过程中遇到的问题和问题的解决方案 有些问题可能还没解决,欢迎各位大佬给与提点. 另外,使用本文前可 ...
- 服务器RAID技术基础
RAID ( Redundant Array of Independent Disks )即独立磁盘冗余阵列,通常简称为磁盘阵列 一.RAID主要优势 大容量: 这是 RAID 的一个显然优势,它扩大 ...
- Object类和@Data注解
特别说明:若是有不对的地方欢迎指正 简要概述: Object类是java中所有类默认继承的一个类.下面介绍一下Object类中的一些重要的方法,面试中也是经常会被问到的.尤其是==和equals的区别 ...
- 用正则表达式来验证QQ号是否合法
import re #首先我们定义一个函数利用正则表达式来获取QQ号 def testQQ(qq): pattern = re.compile('[1-9][0-9]{4,10}$') result ...
- linux中RabbitMQ安装教程
linux中RabbitMQ安装教程 在做一个微服务项目时候用到消息队列,于是深入了解了消息队列知识,并在linux上安装了Rabbitmq,本博客介绍Rabbitmq的安装教程,想要深入了解消息队列 ...
- chrome浏览器通过ajax的POST请求报403解决方法
方法1 把POST改成GET方式 方法2 添加请求格式contentType:“application/json”,
- Mysql数据库优化一:集群(读写分离)之主从服务器的安装与配置
Mysql数据库的集群(读写分离),说白了就是将读操作和写操作分开在不同的服务器上实现,以达到提高效率的目的. 大致原理如下: 数据库中的所有操作都是有日志记录的(前提是要打开这个日志记录功能) 1. ...