题目传送门

题目大意

给出一个 \(n\) 个点的树,每个点有权值,从中选出一些点,使得满足大根堆的性质。(即一个点的祖先节点如果选了那么该点的祖先节点的权值一定需要大于该点权值)

问能选出来的大根堆的最大大小。\(n\le 2\times 10^5\)

线段树合并

跟尧姐一起想的。

首先不难想到 dp,我们可以设 \(f_{u,i}\) 表示 \(u\) 子树内选出顶点权值 \(\le i\) 的大根堆的最大大小。我们可以列出转移式:

\[f_{u,i}=\max\{\sum_{v\in son_u} f_{v,i},\sum_{v\in son_u} f_{v,i-1}+1\}
\]

然后我们发现这个东西似乎可以线段树合并转移。具体来说我们每次先把儿子的线段树合并起来,然后计算以当前节点为根的大根堆的最大大小。

不难发现,我们需要实现区间取 \(\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月赛」大根堆的更多相关文章

  1. BZOJ4919[Lydsy1706月赛]大根堆-------------线段树进阶

    是不是每做道线段树进阶都要写个题解..根本不会写 Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切 ...

  2. bzoj4919 [Lydsy1706月赛]大根堆

    Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质: ...

  3. BZOJ4919:[Lydsy1706月赛]大根堆(set启发式合并)

    Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质: ...

  4. BZOJ4919 [Lydsy1706月赛]大根堆 【dp + 启发式合并】

    题目链接 BZOJ4919 题解 链上的\(LIS\)维护一个数组\(f[i]\)表示长度为\(i\)的\(LIS\)最小的结尾大小 我们可以用\(multiset\)来维护这个数组,子树互不影响,启 ...

  5. BZOJ4921「Lydsy1706月赛」互质序列

    吐槽一下BZOJ没有C++11  题还是不难的 BZOJ 4921 题意 在长度为$ n$的数列中去掉非空的连续一段并保证剩下数字不少于$ 2$ 求合法的所有方案中剩下数字的最大公约数的总和 $Sol ...

  6. 题解 「BJOI2018 治疗之雨」

    题目传送门 题目大意 有一个初始为 \(p\) 的数,每次操作分为以下两个: 有 \(\frac{1}{m+1}\) 的概率$+1,但是中途 \(p\) 的最大值只能为 \(n\)$ 有 \(k\) ...

  7. 【BZOJ4919】[Lydsy六月月赛]大根堆 线段树合并

    [BZOJ4919][Lydsy六月月赛]大根堆 Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切 ...

  8. bzoj 4919 [Lydsy1706月赛]大根堆 set启发式合并+LIS

    4919: [Lydsy1706月赛]大根堆 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 599  Solved: 260[Submit][Stat ...

  9. [Lydsy1706月赛]大根堆

    4919: [Lydsy1706月赛]大根堆 Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 358  Solved: 150[Submit][Stat ...

随机推荐

  1. ES6扩展——数值扩展

    1.0o代表八进制 0b代表二进制 ,通过Number()可转为10进制: //0o 0O octanary八进制 //0b 0B binary二进制 console.log(0o16); //14 ...

  2. configparser读

    #-*-coding:utf-8-*-__author__ = "logan.xu"import configparserconf = configparser.ConfigPar ...

  3. ❤️用武侠小说的形式来阅读LinkedList的源码,绝了!

    一.LinkedList 的剖白 大家好,我是 LinkedList,和 ArrayList 是同门师兄弟,但我俩练的内功却完全不同.师兄练的是动态数组,我练的是链表. 问大家一个问题,知道我为什么要 ...

  4. linux 下 I/O 多路复用初探

    本文内容整理自B站up主 free-coder 发布的视频:[并发]IO多路复用select/poll/epoll介绍 引入 一般来讲,服务器在处理IO请求(一般指的是socket编程)时,需要对so ...

  5. Git - 命令行 常用

    一.合并其他分支的commit(A分支中的commit合并至B分支) 切换到A分支,查询commit历史命令行 : $ git log 复制要合并的commit id (如:663802dfb121e ...

  6. XSS注入

    XSS 原理: 程序对输入和输出没有做合适的处理,导致"精心构造"的字符输出在前端时被浏览器当作有效代码解析执行从而产生危害. 分类 : 危害:存储型 > 反射型 > ...

  7. pymysql基础教程

    pymysql基础教程 1.下载pymysql 在命令框输入指令即可 pip install pymysql 2.连接pymysql 连接数据库: import pymysql conn = pymy ...

  8. 学习PHP中的国际化功能来查看货币及日期信息

    做为一门在世界范围内广泛使用的编程语言,国际化能力往往是衡量一个编程语言是否能够大范围流行的重要内容.特别是对于 PHP 这种以 Web 页面编程为主战场的语言来说,国际化能力更是重中之重.在 PHP ...

  9. 深入学习Composer原理(四)

    本系列第四篇文章,也是最后一篇 首先,我们先看看Composer的源码从哪里看起.当然,请您先准备好源码. composer init或者直接install之后,自动生成了一个vendor目录,这时您 ...

  10. PHPMailer实现发送邮件的方法介绍

    来自: https://www.php.cn/php-weizijiaocheng-408762.html PHPmailer请在github下载,或者直接百度,也不难,虽然PHPmailer里面一大 ...