题解 「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 ...
随机推荐
- golang——rune
byte 等同于int8,常用来处理ascii字符 rune等同于int32,常用来处理unicode或utf-8字符//可以处理中文
- LeetCode入门指南 之 二分搜索
上图表示常用的二分查找模板: 第一种是最基础的,查找区间左右都为闭区间,比较后若不等,剩余区间都不会再包含mid:一般在不需要确定目标值的边界时,用此法即可. 第二种查找区间为左闭右开,要确定targ ...
- system的使用
<stdio.h> std是一个标准库,i =input o =output 标准输入输出库 .h头文件 system的使用 功能:在已经运行的程序 ...
- 后端拜拜,用不到你了,前端开发数据模拟神器nodejs
后端拜拜,用不到你了,前端开发数据模拟神器nodejs 欢迎关注博主公众号「java大师」, 专注于分享Java领域干货文章, 关注回复「资源」, 免费领取全网最热的Java架构师学习PDF, 转载请 ...
- K8s配置。--未完成
配置K8s ##################################################################################### # 配置 --- ...
- WebService学习总结(六)--CXF 与Spring结合+tomcat发布
该项目在上文 WebService学习总结(四)--基于CXF的服务端开发 的基础上修改为spring上发布的webservice接口 1.新建web project 工程 2.导入spring ...
- Python3正则表达式学习笔记
学习前准备:导入re模块 import re 一.re的核心函数 1 - re.compile(pattern[, flags]) 编译正则表达式,速度快 2 - re.match(pattern, ...
- com.google.zxing.NotFoundException-识别图片二维码信息错误
一.问题由来 自己在做一个小程序项目的后台,其中需要使用到识别图片二维码信息,而且是必须在Java后台进行识别二维码操作.去百度里面很快找到一个方法, 可以识别简单的二维码,而且自己生成的简单的二维码 ...
- TypeScript 中命名空间与模块的理解?区别?
一.模块 TypeScript 与ECMAScript 2015 一样,任何包含顶级 import 或者 export 的文件都被当成一个模块 相反地,如果一个文件不带有顶级的import或者expo ...
- 苹果ASA广告投放归因的接入
前段时间,苹果终于在大陆区开放了应用商店的竞价广告.毫无疑问又开启了苹果应用导量的新玩法,各大厂商都紧跟脚步吃螃蟹.本篇讲解苹果广告中的归因部分. 苹果广告其实在海外已运行多年,而因为IDFA的政策变 ...