题解 「BZOJ4919 Lydsy1706月赛」大根堆
题目大意
给出一个 \(n\) 个点的树,每个点有权值,从中选出一些点,使得满足大根堆的性质。(即一个点的祖先节点如果选了那么该点的祖先节点的权值一定需要大于该点权值)
问能选出来的大根堆的最大大小。\(n\le 2\times 10^5\)
线段树合并
跟尧姐一起想的。
首先不难想到 dp,我们可以设 \(f_{u,i}\) 表示 \(u\) 子树内选出顶点权值 \(\le i\) 的大根堆的最大大小。我们可以列出转移式:
\]
然后我们发现这个东西似乎可以线段树合并转移。具体来说我们每次先把儿子的线段树合并起来,然后计算以当前节点为根的大根堆的最大大小。
不难发现,我们需要实现区间取 \(\max\),单点查询。这个东西如果要硬上的话似乎是不好搞的,因为线段树合并似乎不支持懒标记(此处存疑)。
接着思考,可以发现的是,我们每次修改的一定是一段区间,而且如果能够修改肯定是区间 \(+1\)。于是,我们就可以维护差分数组,然后在线段树上二分找到修改的端点即可。
时间复杂度 \(\Theta(n\log n)\),空间复杂度 \(\Theta(n\log n)\)。
\(\texttt{Code}\)
#include <bits/stdc++.h>
using namespace std;
#define Int register int
#define INF 0x7f7f7f7f
#define MAXN 200005
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
vector <int> G[MAXN];
int n,uni,res,f[MAXN],rt[MAXN],tmp[MAXN],value[MAXN];
struct Segment{
#define MAXM MAXN*100
int cnt,son[MAXM][2],sum[MAXM];
void modify (int &x,int l,int r,int pos,int val){
if (!x) x = ++ cnt;sum[x] += val;
if (l == r) return ;
int mid = (l + r) >> 1;
if (pos <= mid) modify (son[x][0],l,mid,pos,val);
else modify (son[x][1],mid + 1,r,pos,val);
}
int query (int x,int l,int r,int ql,int qr){
if (!x) return 0;
if (ql > qr) return 0;
if (l >= ql && r <= qr) return sum[x];
int mid = (l + r) >> 1,res = 0;
if (ql <= mid) res += query (son[x][0],l,mid,ql,qr);
if (qr > mid) res += query (son[x][1],mid + 1,r,ql,qr);
return res;
}
int Merge (int x,int y){
if (!x || !y) return x + y;
sum[x] += sum[y];
son[x][0] = Merge (son[x][0],son[y][0]);
son[x][1] = Merge (son[x][1],son[y][1]);
return x;
}
int find (int x,int l,int r,int t){
if (!x) return r;
if (l == r) return l;
int mid = (l + r) >> 1;
if (t < sum[son[x][0]]) return find (son[x][0],l,mid,t);
else return find (son[x][1],mid + 1,r,t - sum[son[x][0]]);
}
}Tree;
void dfs (int u){
f[u] = 1;
for (Int v : G[u]){
dfs (v);
rt[u] = Tree.Merge (rt[u],rt[v]);
}
f[u] = Tree.query (rt[u],1,uni,1,value[u] - 1) + 1;
Tree.modify (rt[u],1,uni,value[u],1);
int pos = Tree.find (rt[u],1,uni,f[u]);
if (Tree.query (rt[u],1,uni,1,pos) > f[u]) -- pos;
if (pos < uni) Tree.modify (rt[u],1,uni,pos + 1,-1);
}
signed main(){
read (n);
for (Int i = 1,fa;i <= n;++ i) read (value[i],fa),G[fa].push_back (i),tmp[i] = value[i];
sort (tmp + 1,tmp + n + 1),uni = unique (tmp + 1,tmp + n + 1) - tmp - 1;
for (Int i = 1;i <= n;++ i) value[i] = lower_bound (tmp + 1,tmp + uni + 1,value[i]) - tmp;
dfs (1);write (Tree.query (rt[1],1,uni,1,uni)),putchar ('\n');
return 0;
}
set 启发式合并
by @自为风月马前卒
本质上来说是个贪心???
可以发现的是,在大根堆大小相同的时候,我们肯定想要顶点的权值尽可能小,这样就对后面合并更优。
于是,我们可以考虑把当前点与子树进行合并,我们如果子树内有比当前点更大的值,我们发现答案不会变,直接替换 set 中比它与它相邻的树即可。否则直接加进来即可。
时间复杂度 \(\Theta(n\log^2n)\),空间复杂度 \(\Theta(n)\)。
\(\texttt{Code}\)
#include<bits/stdc++.h>
#define sit multiset<int>::iterator
using namespace std;
const int MAXN = 2e5 + 10;
inline int read() {
char c = getchar(); int x = 0, f = 1;
while(c < '0' || c > '9') {if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * f;
}
vector<int> v[MAXN];
multiset<int> s[MAXN];
int N, val[MAXN];
void dfs(int x, int fa) {
for(int i = 0; i < v[x].size(); i++) {
int to = v[x][i];
if(to == fa) continue;
dfs(to, x);
if(s[to].size() > s[x].size()) swap(s[to], s[x]);
for(sit it = s[to].begin(); it != s[to].end(); it++)
s[x].insert(*it);
s[to].clear();
}
sit it = s[x].lower_bound(val[x]);
if(it != s[x].end()) s[x].erase(it);
s[x].insert(val[x]);
}
main() {
N = read();
for(int i = 1; i <= N; i++) {
val[i] = read();
int x = read();
v[i].push_back(x); v[x].push_back(i);
}
dfs(1, 0);
printf("%d", s[1].size());
return 0;
}
题解 「BZOJ4919 Lydsy1706月赛」大根堆的更多相关文章
- BZOJ4919[Lydsy1706月赛]大根堆-------------线段树进阶
是不是每做道线段树进阶都要写个题解..根本不会写 Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切 ...
- bzoj4919 [Lydsy1706月赛]大根堆
Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质: ...
- BZOJ4919:[Lydsy1706月赛]大根堆(set启发式合并)
Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质: ...
- BZOJ4919 [Lydsy1706月赛]大根堆 【dp + 启发式合并】
题目链接 BZOJ4919 题解 链上的\(LIS\)维护一个数组\(f[i]\)表示长度为\(i\)的\(LIS\)最小的结尾大小 我们可以用\(multiset\)来维护这个数组,子树互不影响,启 ...
- BZOJ4921「Lydsy1706月赛」互质序列
吐槽一下BZOJ没有C++11 题还是不难的 BZOJ 4921 题意 在长度为$ n$的数列中去掉非空的连续一段并保证剩下数字不少于$ 2$ 求合法的所有方案中剩下数字的最大公约数的总和 $Sol ...
- 题解 「BJOI2018 治疗之雨」
题目传送门 题目大意 有一个初始为 \(p\) 的数,每次操作分为以下两个: 有 \(\frac{1}{m+1}\) 的概率$+1,但是中途 \(p\) 的最大值只能为 \(n\)$ 有 \(k\) ...
- 【BZOJ4919】[Lydsy六月月赛]大根堆 线段树合并
[BZOJ4919][Lydsy六月月赛]大根堆 Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切 ...
- bzoj 4919 [Lydsy1706月赛]大根堆 set启发式合并+LIS
4919: [Lydsy1706月赛]大根堆 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 599 Solved: 260[Submit][Stat ...
- [Lydsy1706月赛]大根堆
4919: [Lydsy1706月赛]大根堆 Time Limit: 10 Sec Memory Limit: 256 MBSubmit: 358 Solved: 150[Submit][Stat ...
随机推荐
- OVN入门
参考链接 如何借助 OVN 来提高 OVS 在云计算环境中的性能 OVN简介 Open vSwitch Documentation OVSDB介绍及在OpenDaylight中的调用 OpenDayl ...
- @RequestParam注解的详细介绍
@RequestParam (org.springframework.web.bind.annotation.RequestParam)用于将指定的请求参数赋值给方法中的形参. 有三个属性: (1)v ...
- PowerDotNet平台化软件架构设计与实现系列(01):基础数据平台
本系列我将主要通过图片和少许文字讲解通过个人自研的PowerDotNet进行快速开发平台化软件产品. PowerDotNet不仅仅是包含像Newtonsoft.Json.Dapper.Quartz.R ...
- centos7 权限更改,所属用户及用户组更改
2021-08-03 # 查看文件的权限 ll 第一个字符, "-" 表示是文件, "d" 表示是目录(directory) 后面 9 个字符每 3 个字符又作 ...
- Linux系统下的软件管理(rpm)、搭建第三方软件库、yum的黑名单
对wps-office进行模糊匹配照样可以查找出该软件yum clean all ? ? ? ?##清空yum缓存识别新配置 测验安装wps软件: 安装成功即可使用办公软件 1.yum install ...
- java设计模式—单例模式(包含单例的破坏)
什么是单例模式? 保证一个了类仅有一个实例,并提供一个访问它的全局访问点. 单例模式的应用场景? 网站的计数器,一般也是采用单例模式实现,否则难以同步: Web应用的配置对象的读取,一般也应用单例模式 ...
- Python图像分割之区域增长法
原文链接:https://blog.csdn.net/sgzqc/article/details/119682864 一.简介 区域增长法是一种已受到计算机视觉界十分关注的图像分割方法.它是以区域为处 ...
- Flex语法和常用鼠标手势
Flex弹性和模型 1.display : flex/inline-flex ;(设置给氟元素) flex : 将对象作为弹性伸缩盒显示: inline-flex : 将对象作为内联块级弹性伸缩显示: ...
- 逐条更新数据 sql
declare @tid int declare @fid int declare @i int declare @j int set @j=(select count(*) from ...
- 【OWASP TOP10】2021年常见web安全漏洞TOP10排行
[2021]常见web安全漏洞TOP10排行 应用程序安全风险 攻击者可以通过应用程序中许多的不同的路径方式去危害企业业务.每种路径方法都代表了一种风险,这些风险都值得关注. 什么是 OWASP TO ...