题目链接

考虑树退化为链的情况,就是求一个最长(严格)上升子序列。

对于树,不同子树间是互不影响的。仿照序列上的LIS,对每个点x维护一个状态集合,即合并其子节点后的集合,然后用val[x]替换掉第一个大于它的数(有等于的就不换了)。

最后根节点状态集合的大小就是答案了。

关于替换数,可以先找到这个数的位置,如果有这个数就不用管了;没有的话插入进去,然后递归回去,找到一个靠右的位置删掉。

当然其实不用线段树合并这么麻烦,直接上multiset启发式合并就可以了。。

注意是合并了子树的状态,so叶节点size>1也是可能的!不要直接置为0!(应该-=1)

线段树合并比set启发式合并慢多了。。。

DFS可以用for一遍n~1取代。

另外有另一种线段树合并的方法,要区间修改、标记永久化,这里大体记一下具体步骤:

以下是口胡,建议忽略。

\(x\)从子树转移,就是\(f[x][j]=f[v1][j]+f[v2][j]+...\),对应位置相加;最后转移完对区间\(val_x\sim INF\)和\(f[x][val_x-1]+1\)取\(\max\)。

\(CQzhangyu\)的代码里\(tag\)是区间取\(\max\)的标记,\(sum\)是区间加的标记。\(sum\)和正常一样在访问节点前下传。\(tag\)为什么感觉不标记永久化一样下传也可以啊...当然也没必要下传。

合并的时候,假设现在是把\(y\)合并到\(x\)上(是对应位置做加法)。

如果\(lson[x]=0\),那说明\(lson[x]\)的最大值就是\(x\)的最大值\(tag[x]\),所以之前\(x\)上的给\(lson[x]\)的区间加标记\(sum[x]\)就不用管了。(\(x\)节点上的最大值是已经被\(sum\)更新后的)

所以如果\(lson[x]=0,\ lson[y]\neq0\),合并是对应位置相加,所以\(lson[x]\)的值应该变成\(lson[x]+lson[y]\)。

而现在\(lson[x]=0\),而\(tag[lson[x]]\)其实等于\(tag[x]\),所以对应位置相加后,现在的\(tag[lson[x]]\)就变成\(tag[x]+tag[lson[y]]\)。

\(lson[y]\)的区间加标记可能没下传,要保留,现在加上\(lson[x]\)的值就变成了\(tag[x]+sum[lson[y]]\)。

其它时候同理吧...

//64956kb	1288ms
#include <cstdio>
#include <cctype>
#include <cassert>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 300000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
const int N=2e5+5; int n,A[N],cnt,ref[N],root[N],Enum,H[N],nxt[N],to[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Segment_Tree
{
#define S N*19
#define lson son[x][0]
#define rson son[x][1]
int tot,sz[S],son[S][2];
bool flag; #define Update(x) sz[x]=sz[lson]+sz[rson]
void Delete(int x,int l,int r)
{
if(l==r)
{
flag=0, --sz[x];//not sz[x]=0!
return;
}
if(sz[lson]) Delete(lson,l,l+r>>1);
else/*if(sz[rson])*/ Delete(rson,(l+r>>1)+1,r);
Update(x);
}
void Insert(int &x,int l,int r,int p)
{
if(!x) x=++tot;
if(l==r)
{
if(!sz[x]) flag=1, ++sz[x];
return;
}
int m=l+r>>1;
if(p<=m)
{
Insert(lson,l,m,p);
if(flag&&sz[rson]) Delete(rson,m+1,r);//新建的时候肯定删不了啊(没有右子树)
}
else Insert(rson,m+1,r,p);
Update(x);
}
int Merge(int x,int y)
{
if(!x||!y) return x^y;
lson=Merge(lson,son[y][0]), rson=Merge(rson,son[y][1]);
sz[x]+=sz[y], sz[y]=0; return x;
}
}T; inline int read()
{
int now=0;register char c=gc();
for(;!isdigit(c);c=gc());
for(;isdigit(c);now=now*10+c-'0',c=gc());
return now;
}
inline void AddEdge(int u,int v){
/*if(u)*/ to[++Enum]=v, nxt[Enum]=H[u], H[u]=Enum;
}
int Find(int x)
{
int l=1, r=cnt, mid;
while(l<r)
if(ref[mid=l+r>>1]<x) l=mid+1;
else r=mid;
return l;
}
void DFS(int x)
{
for(int i=H[x]; i; i=nxt[i])
DFS(to[i]), root[x]=T.Merge(root[x],root[to[i]]);
T.flag=0, T.Insert(root[x],1,cnt,A[x]);
} int main()
{
n=read();
for(int i=1; i<=n; ++i) ref[i]=A[i]=read(), AddEdge(read(),i); std::sort(ref+1,ref+1+n), cnt=1;
for(int i=2; i<=n; ++i) if(ref[i]!=ref[i-1]) ref[++cnt]=ref[i];
for(int i=1; i<=n; ++i) A[i]=Find(A[i]); DFS(1), printf("%d\n",T.sz[root[1]]); return 0;
}

BZOJ.4919.[Lydsy1706月赛]大根堆(线段树合并/启发式合并)的更多相关文章

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

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

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

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

  3. BZOJ 4919: [Lydsy1706月赛]大根堆 启发式合并

    我不会告诉你这是线段树合并的好题的... 好吧我们可以搞一个multiset在dfs时求出LIS(自带二分+排序)进行启发式合并,轻松加愉悦... #include<cstdio> #in ...

  4. BZOJ 4919: [Lydsy1706月赛]大根堆

    F[x][i]表示x的子树中取的数字<=i的最大值,线段树合并优化DP 写得很难看,并不知道好看的写法 #include<cstdio> #include<algorithm& ...

  5. BZOJ 4919: [Lydsy1706月赛]大根堆 set启发式合并

    这个和 bzoj 5469 几乎是同一道题,但是这里给出另一种做法. 你发现你要求的是一个树上 LIS,而序列上的 LIS 有一个特别神奇的 $O(n\log n) $ 做法. 就是维护一个单调递增的 ...

  6. BZOJ 4919 [Lydsy1706月赛]大根堆 (SRM08 T3)

    [题解] 求一个序列的LIS有一个二分做法是这样的:f[i]表示长度为i的上升序列中最后一个数最小可以是多少,每次二分大于等于当前数字x的f[j],把f[j]修改为x:如果找不到这样的f[j],那就把 ...

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

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

  8. [Lydsy1706月赛]大根堆

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

  9. bzoj4919 [Lydsy1706月赛]大根堆

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

随机推荐

  1. Java 8 Lambda表达式,让你的代码更简洁

    Lambda表达式是Java 8一个非常重要的新特性.它像方法一样,利用很简单的语法来定义参数列表和方法体.目前Lambda表达式已经成为高级编程语言的标配,像Python,Swift等都已经支持La ...

  2. Go语言知识点笔记

    golang的花括号: 在go中,继承了C系的花括号作为一个作用域块的包含范围指示,但不同于C/C++中花括号位置可任意摆放,go要求“ { ”必须在右侧(一行代码尾部),不能单独另起一行.类似Pyt ...

  3. gradle 构建工具,与Ant Maven关系

    1   基本概念 gradle是一个基于Apache ant 和apache maven概念的项目自动化建构工具.它使用一种基于Groovy的特定领域语言来声明项目设置,而不是传统的xml.当前其支持 ...

  4. 修复TortoiseGit文件夹和文件图标不显示问题的多种解决办法以及重启之后TortoiseGit图标注册表又不见了的解决办法

    一 首先进行第一种尝试 打开 regedit.exe ,准备修改注册表 找到 HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion\ ...

  5. DRM/KMS 基本组件介绍

    Each DRM device provides access to manage which monitors and displays are currently used and what fr ...

  6. C# 应用程序配置文件App.Config和web.config

    应用程序配置文件,对于asp.net是 web.config,对于WINFORM程序是 App.Config(ExeName.exe.config). 配置文件,对于程序本身来说,就是基础和依据,其本 ...

  7. [转载]如何在C++03中模拟C++11的右值引用std::move特性

    本文摘自: http://adamcavendish.is-programmer.com/posts/38190.htm 引言 众所周知,C++11 的新特性中有一个非常重要的特性,那就是 rvalu ...

  8. Session和Cookie,Django的自动登录机制

    什么是Cookie? Cookie是浏览器的本地存储机制,存储服务器返回的各种信息,下次发起请求时再发送给服务端,比如访问baidu 什么是Session? 刚才说道,Cookie存储服务端返回的信息 ...

  9. HTML网页自动跳转

    <meta http-equiv="refresh" content="3;URL=res.html">

  10. 用户说体验 | 关于阿里百川HotFix你需要了解的一些细节

    最近很火的热修复技术,无意中了解到阿里百川也在做,而且Android.iOS两端都支持,所以决定试一试.试用一段时间后,感觉还不错,主要是他们有一个团队在不断维护更新这个产品,可以看到他们的版本更新记 ...