Description

题库链接

给定一棵 \(n\) 个节点的有根树,每个点有一个权值 \(val_i\) 。你需要选择尽可能多的节点,使得:对于任意两个点 \(i,j\) ,如果 \(i\) 在树上是 \(j\) 的祖先,那么 \(v_i>v_j\) 。请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。

\(1\leq n\leq 200000\)

Solution

记 \(f_{u,i}\) 表示在 \(u\) 节点的子树中选取的最大的点权为 \(i\) 的方案最大值。

那么转移就是枚举其子树中的状态,并在其它的子树中找到点权小于等于其的最大的方案值。

这样是 \(O(n^2)\) 的,考虑优化更新过程。

容易发现,转移时就是用一个前缀最大值更新一个后缀,用线段树维护,合并节点信息时启发式合并即可。

复杂度为 \(O(n\log_2^2 n)\) 的。

Code

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
const int N = 200000, inf = ~0u>>1; int n, val[N+5], f, b[N+5], tot, size[N+5], s[N+5][3], top;
vector<int>to[N+5];
struct Segment_tree {
int root[N+5], ch[N*50+5][2], maxn[N*50+5], tag[N*50+5], pos;
void pushdown(int o) {
tag[ch[o][0]] += tag[o], tag[ch[o][1]] += tag[o];
maxn[ch[o][0]] += tag[o], maxn[ch[o][1]] += tag[o];
tag[o] = 0;
}
void get(int o, int l, int r) {
if (!o) return;
if (l == r) {s[++top][0] = l, s[top][1] = maxn[o]; return; }
if (tag[o]) pushdown(o); int mid = (l+r)>>1;
get(ch[o][0], l, mid); get(ch[o][1], mid+1, r);
}
void update(int &o, int l, int r, int loc, int val) {
if (!o) o = ++pos; maxn[o] = max(maxn[o], val);
if (l == r) return;
if (tag[o]) pushdown(o); int mid = (l+r)>>1;
if (loc <= mid) update(ch[o][0], l, mid, loc, val);
else update(ch[o][1], mid+1, r, loc, val);
}
void modify(int o, int l, int r, int a, int b, int val) {
if (!o || a > b) return;
if (a <= l && r <= b) {tag[o] += val, maxn[o] += val; return; }
if (tag[o]) pushdown(o); int mid = (l+r)>>1;
if (a <= mid) modify(ch[o][0], l, mid, a, b, val);
if (b > mid) modify(ch[o][1], mid+1, r, a, b, val);
maxn[o] = 0;
if (ch[o][0]) maxn[o] = max(maxn[ch[o][0]], maxn[o]);
if (ch[o][1]) maxn[o] = max(maxn[ch[o][1]], maxn[o]);
}
int query(int o, int l, int r, int a, int b) {
if (!o || a > b) return 0;
if (a <= l && r <= b) return maxn[o];
if (tag[o]) pushdown(o); int mid = (l+r)>>1, c1 = 0, c2 = 0;
if (a <= mid) c1 = query(ch[o][0], l, mid, a, b);
if (b > mid) c2 = query(ch[o][1], mid+1, r, a, b);
return max(c1, c2);
}
}T; void dfs(int u) {
for (int i = 0, sz = to[u].size(), v; i < sz; i++) {
dfs(v = to[u][i]);
if (size[u] == 0) T.root[u] = T.root[v];
else {
int a = u, b = v;
if (size[a] > size[b]) swap(a, b);
top = 0; T.get(T.root[a], 1, tot);
for (int j = 1; j <= top; j++) {
s[j][2] = max(s[j-1][2], s[j][1]);
s[j][1] += T.query(T.root[b], 1, tot, 1, s[j][0]);
}
for (int j = 1; j <= top; j++) T.modify(T.root[b], 1, tot, s[j][0]+1, (j == top ? tot : s[j+1][0]), s[j][2]);
for (int j = 1; j <= top; j++) T.update(T.root[b], 1, tot, s[j][0], s[j][1]);
T.root[u] = T.root[b];
}
size[u] += size[v];
}
++size[u];
T.update(T.root[u], 1, tot, val[u], T.query(T.root[u], 1, tot, 1, val[u]-1)+1);
}
void work() {
scanf("%d", &n); b[++tot] = val[0] = inf;
for (int i = 1; i <= n; i++) {
scanf("%d%d", &val[i], &f);
to[f].pb(i); b[++tot] = val[i];
}
sort(b+1, b+tot+1); tot = unique(b+1, b+tot+1)-b-1;
for (int i = 0; i <= n; i++) val[i] = lower_bound(b+1, b+tot+1, val[i])-b;
dfs(0); printf("%d\n", T.query(T.root[0], 1, tot, tot, tot)-1);
}
int main() {work(); return 0; }

[BZOJ 4919]大根堆的更多相关文章

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

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

  2. bzoj 4919: [Lydsy六月月赛]大根堆

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

  3. bzoj 1577: [Usaco2009 Feb]庙会捷运Fair Shuttle——小根堆+大根堆+贪心

    Description 公交车一共经过N(1<=N<=20000)个站点,从站点1一直驶到站点N.K(1<=K<=50000)群奶牛希望搭乘这辆公交车.第i群牛一共有Mi(1& ...

  4. bzoj 5495: [2019省队联测]异或粽子【可持久化trie+大根堆】

    和bzoj4504差不多,就是换了个数据结构 像超级钢琴一样把五元组放进大根堆,每次取一个出来拆开,(d,l,r,p,v)表示右端点为d,左端点区间为(l,r),最大区间和值为v左端点在p上 关于怎么 ...

  5. bzoj 4504: K个串【大根堆+主席树】

    像超级钢琴一样把五元组放进大根堆,每次取一个出来拆开,(d,l,r,p,v)表示右端点为d,左端点区间为(l,r),最大区间和值为v左端点在p上 关于怎么快速求区间和,用可持久化线段树维护(主席树?) ...

  6. [Lydsy1706月赛]大根堆

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

  7. Java实现堆排序(大根堆)

    堆排序是一种树形选择排序方法,它的特点是:在排序的过程中,将array[0,...,n-1]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子结点之间的内在关系,在当前无序区中选择关键 ...

  8. bzoj4919 [Lydsy1706月赛]大根堆

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

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

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

随机推荐

  1. 分区表SQL调优/优化(Tuning)时容易“被欺骗”的场景之一

    近几天没有用户找到,除了看看书,就是上网浏览点东西,好不惬意.可惜好景不长,正在享受悠闲惬意的日子时,一个用户的工作人员QQ找到我,说他们在统计一些数据,但一个SQL特别慢,或者说就从来没出过数据,我 ...

  2. C语言程序设计(基础)- 第0次作业

    亲爱的同学们,恭喜你成为一名大学生,我也很荣幸能够带大家一起学习大学的第一门专业基础课.还在军训的你,肯定对大学生活和计算机专业有着美好的憧憬,那么大学生活是什么样子的那?计算机专业应该怎么学习那?请 ...

  3. Alpha冲刺No.4

    冲刺Day4 一.站立式会议 本来还想今天下午好好弄弄安卓开发,结果计划赶不上变化.(不存在的) 完成备忘录设计,个人界面设计 二.实际项目进展 搞了404(安卓和ssm的连接),好像还是不太行. 备 ...

  4. PostgreSQL 配置安装

    Mac 安装 http://postgresapp.com/ 创建和删除数据库用户 对应命令如下(在postgres=# 环境下):1.查看数据库用户列表: \du2.创建数据库用户: create ...

  5. Android Notification setLatestEventInfo方法已废弃

    代替setLatestEventInfo的方法是用Notification.Builder创建Builder对象,通过该对象设置Notification相关属性. otification.Builde ...

  6. PC或者手机弹出窗效果

    http://layer.layui.com/ 这个网站提供弹窗,是在jq封装的,弹窗之后,背景页面还可以滑动. 这个里面的js可能也会包含css,这个css不能移动位置,否则会报错,还有谷歌浏览器在 ...

  7. IIS 配置 FTP 网站

    在 服务器管理器 的 Web服务器IIS 上安装 FTP 服务 在 IIS管理器 添加FTP网站 配置防火墙规则 说明:服务器环境是Windows Server 2008 R2,IIS7.5. 1. ...

  8. 一张图说明 Web Api 参数绑定默认规则

    请求如下: 控制器如下: 慎重说明:不管请求方式是 get 还是 post , 简单类型的参数,如 name 和 id ,其值都是从 url 里面去取. Web API 从 url 还是 body 获 ...

  9. css3兼容IE8的方案 各个ie的hack

    虽然现在很多项目已经对低版本IE不要求了,但是还有部分公司对IE8还是很执着的,咱作为屌丝前端程序员不能和老板说前端潮流,不能说趋势,只能动脑子了,下面就分享一些css3兼容ie8的方案思路.主要是实 ...

  10. kubernetes入门(09)kubernetes1.7集群安装(2017/11/13)

    CentOS7.3利用kubeadm安装kubernetes1.7.3完整版(官方文档填坑篇) https://www.cnblogs.com/liangDream/p/7358847.html 一. ...