BZOJ4919[Lydsy1706月赛]大根堆-------------线段树进阶
是不是每做道线段树进阶都要写个题解。。根本不会写
Description
给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点。每个点有一个权值v_i。 你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j。 请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。
Solution
看起来很像一道DP,实际上也确实要用到DP的思想。
设编号为i的点的子树中最大权值为j的最优方案为fi,j,i的权值为vi
在线段树中维护f,进行标记永久化,不需要进行pushup,在求和时累加路径上的变化量即可。
那么我们就能在对树DFS的过程中进行对f的更新。在每次搜到叶节点后开线段树,回溯时进行线段树合并,并进行对f的维护。
对fi的转移,有取i点和不取i点两种情况。不取i时f最小值为子树中最大权值为v[i]的方案总和,取i时为子树中最大权值为v[i]-1的方案总和数+1。
若不取i的最小值大于取i最大值,则直接return
反之则利用f与v的单调正相关的关系进行二分,找到最优决策点并在线段树中更新即可。
具体看代码
Code:

1 #include<bits/stdc++.h>
2 #define debug cout<<"wrong"<<endl
3 using namespace std;
4 const int NN=2e5+10;
5 int rt[NN],to[NN],nex[NN],head[NN],v[NN],num,ext,n;
6 inline int read(){
7 int x=0,f=1;
8 char ch=getchar();
9 while(ch<'0'||ch>'9'){
10 if(ch=='-') f=-1;
11 ch=getchar();
12 }
13 while(ch<='9'&&ch>='0'){
14 x=(x<<1)+(x<<3)+(ch^48);
15 ch=getchar();
16 }
17 return x*f;
18 }
19 inline void add(int a,int b){
20 to[++num]=b; nex[num]=head[a]; head[a]=num;
21 }
22 void write(int x){
23 if(x<0) putchar('-'), x=-x;
24 if(x>9) write(x/10);
25 putchar(x%10+'0');
26 }
27 void init(){
28 n=read();
29 for(register int i=1;i<=n;i++){
30 v[i]=read();
31 add(read(),i);
32 }
33 int d[NN];
34 for(register int i=1;i<=num;i++) d[i]=v[i];
35 sort(d+1,d+1+num);
36 ext=unique(d+1,d+1+num)-d-1;
37 for(register int i=1;i<=num;i++) v[i]=lower_bound(d+1,d+ext+1,v[i])-d;
38 }
39 struct node{
40 int seg,ls[NN*40],rs[NN*40],num[NN*40];
41 void insert(int &x,int l,int r,int opl,int opr,int val){
42 if(!x) x=++seg;
43 if(opl<=l&&opr>=r){ num[x]+=val; return; }
44 int mid=(l+r)>>1;
45 if(opl<=mid) insert(ls[x],l,mid,opl,opr,val);
46 if(opr>mid) insert(rs[x],mid+1,r,opl,opr,val);
47 }
48 void marge(int &x,int y,int l,int r){
49 if(!x||!y){ x=x+y; return; }
50 num[x]+=num[y];
51 int mid=(l+r)>>1;
52 marge(ls[x],ls[y],l,mid);
53 marge(rs[x],rs[y],mid+1,r);
54 }
55 int query(int x,int l,int r,int pos){
56 if(!x) return 0;
57 int mid=(l+r)>>1,p=num[x];
58 if(pos<=mid) return query(ls[x],l,mid,pos)+p;
59 else return query(rs[x],mid+1,r,pos)+p;
60 }
61 }segt;
62 void dfs(int x){
63 for(register int i=head[x];i;i=nex[i]){
64 dfs(to[i]);
65 segt.marge(rt[x],rt[to[i]],1,ext);
66 }
67 int ans1=segt.query(rt[x],1,ext,v[x]-1)+1;
68 int ans2=segt.query(rt[x],1,ext,v[x]);
69 if(ans1<=ans2) return;
70 int l=v[x],r=ext,mid,pos=v[x];
71 while(l<=r){
72 mid=(l+r)>>1;
73 if(segt.query(rt[x],1,ext,mid)<ans1) l=mid+1, pos=mid;
74 else r=mid-1;
75 }
76 segt.insert(rt[x],1,ext,v[x],pos,1);
77 }
78 int main(){
79 init();
80 dfs(1);
81 write(segt.query(rt[1],1,ext,ext));
82 putchar('\n');
83 return 0;
84 }
Code
BZOJ4919[Lydsy1706月赛]大根堆-------------线段树进阶的更多相关文章
- BZOJ.4919.[Lydsy1706月赛]大根堆(线段树合并/启发式合并)
题目链接 考虑树退化为链的情况,就是求一个最长(严格)上升子序列. 对于树,不同子树间是互不影响的.仿照序列上的LIS,对每个点x维护一个状态集合,即合并其子节点后的集合,然后用val[x]替换掉第一 ...
- 【BZOJ4919】[Lydsy六月月赛]大根堆 线段树合并
[BZOJ4919][Lydsy六月月赛]大根堆 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\)来维护这个数组,子树互不影响,启 ...
- 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 ...
- BZOJ 4919: [Lydsy1706月赛]大根堆 启发式合并
我不会告诉你这是线段树合并的好题的... 好吧我们可以搞一个multiset在dfs时求出LIS(自带二分+排序)进行启发式合并,轻松加愉悦... #include<cstdio> #in ...
- BZOJ 4919: [Lydsy1706月赛]大根堆
F[x][i]表示x的子树中取的数字<=i的最大值,线段树合并优化DP 写得很难看,并不知道好看的写法 #include<cstdio> #include<algorithm& ...
随机推荐
- java基础语法以及进制的转换
关键字 关键字: 被Java语言赋予特定含义的单词 关键字特点 组成关键字的字母全部小写 关键字注意事项 goto和const作为保留字存在,目前并不使用 类似IDEA这样的集成工具,针对关键字有特殊 ...
- 链表 Linked List
目录 链表介绍 单链表 单链表的应用实例 添加-直接添加到末尾 添加-顺序添加 更新 删除 单链表的面试题 双链表 链表介绍 链表时有序的列表,但是它在内存中是存储如下 小结 链表是以节点的方式来存储 ...
- gitlab安装CI问题汇总
0.设置gitlab获取代码的存放位置 vim /etc/gitlab-runner/config.toml 1.unable to access http://gitlab-ci-token:xxx ...
- 图论---DFS
图论---DFS 1. 图的遍历 在理解DFS算法之前,我们首先需要对什么是遍历进行了解,遍历的概念就是:从某一个点出发(一般是首或尾),依次将数据结构中的每一个数据访问且只访问一遍. 2. DFS简 ...
- 创建一个新的解耦的Orchard Core CMS网站
引言本文将介绍创建一个功能齐全.解耦的CMS网站的过程,该网站允许您编辑博客帖子并呈现它们.解耦是一种开发模型,其中站点的前端和后端(管理)托管在同一个Web应用程序中,但只有后端由CMS驱动.然后, ...
- pycharm 增删改查 mysql数据库
1.pycharm创建mysql数据表######################################################import pymysql# 创建连接con = p ...
- 安装配置环境 CUDA以及CUDNN tensorflow pytorch pip安装 虚拟环境
1. 在win10中利用Anaconda直接安装tensorflow-gpu 不需要另行安装cuda cudnn 但是不知道电脑会自动适配所需的版本吗,不过把电脑显卡驱动更新一下,就都也可以了吧. ...
- Android Kotlin协程入门
Android官方推荐使用协程来处理异步问题.以下是协程的特点: 轻量:单个线程上可运行多个协程.协程支持挂起,不会使正在运行协程的线程阻塞.挂起比阻塞节省内存,且支持多个并行操作. 内存泄漏更少:使 ...
- supervisor + celery 的简单配置与报错处理
ubuntu服务器下使用 supervisor 和 celery supervisor 的卸载过程: sudo apt purge supervisor whereis supervisord如果有用 ...
- P6672-[清华集训2016]你的生命已如风中残烛【结论】
正题 题目链接:https://www.luogu.com.cn/problem/P6672 题目大意 长度为\(m\)的序列\(a\),有\(n\)个数字不是\(0\),其他\(m-n\)个是\(0 ...