题目大意:给出一棵有根树(根为 \(0\) ),点有点权。可以删除点(非根),并将其子树接到其父亲上。我们称一个树为树堆当前仅当树上每个点都满足其权值大于等于其子树中所有点的点权。现在对于每个点要求其子树删去一点后形成的树堆的最大大小,其中这个点在其自身的子问题内是不可删除的。

题解

这个每个点的子树都求一遍一眼就可以看出是 线段树合并 或者 dsu on tree ,然后略加思索发现这个维护的过程类似 DP ,可以发现不太适合 dsu on tree ,因此选择 线段树合并 。现在考虑怎么维护。

首先我们将点权离散化,然后每个点开一个线段树用线段树的每个位置维护点权为 \(k\) 的值加在当前点的父亲上(这个新加的点不可删除)时可以得到的最大子树大小。那么对于一个点 \(u\),我们将其子树的线段树全部合并起来,具体来说是线段树的每个节点的权值相加(因为各个子树互不干扰)。点 \(u\) 的答案直接查询其合并完的线段树中 \(val[u]\) 位置的值 \(+1\) 即可得到。但是之后向上合并的时候这个点是可以被删除了,这个要怎么维护?

发现当前的线段树维护的是没加入当前点的,也就是说它维护的还是对于每个权值 \(k\) 其子树可以得到的最大大小,只需要以合适的方法加入 \(u\) 的信息即可。如果上方接入父亲的节点的权值比当前点的大,那么显然可以拿到这个点的答案,但是也可能不选择这个点更优,我们只需要对于线段树的从 \(val[u]\) 开始的后缀的这段区间的所有点对于 \(ans[u]\) 取 \(max\) 即可,但是 \(max\) 和 \(add\) 的标记同时维护起来巨难,无法知晓标记先后顺序。但是不用怕,我们可以把取 \(max\) 转换成加法。发现 \(ans[u]\) 是之前的线段树查询出的结果 \(+1\) ,即线段树上 \(val[u]\) 位置上的值是 \(ans[u]-1\) ,那么我们只需要找到最后一个线段树中权值为 \(ans[u]\) 的位置 \(r\),把区间 \([val[u],r)\) 进行区间 \(+1\) 即可。(因为我们取 \(max\) 是从 \(val[u]\) 这个位置开始的)

具体一些实现细节看代码吧:

#define ll long long
#define db double
#define filein(a) freopen(#a".in","r",stdin)
#define fileot(a) freopen(#a".out","w",stdout)
#define sky fflush(stdout);
#define gc getchar
#define pc putchar
namespace IO{
inline bool blank(const char &c){
return c==' ' or c=='\n' or c=='\t' or c=='\r' or c==EOF;
}
inline void gs(char *s){
char ch=gc();
while(blank(ch) ) {ch=gc();}
while(!blank(ch) ) {*s++=ch;ch=gc();}
*s=0;
}
inline void gs(std::string &s){
char ch=gc();s+='#';
while(blank(ch) ) {ch=gc();}
while(!blank(ch) ) {s+=ch;ch=gc();}
}
inline void ps(char *s){
while(*s!=0) pc(*s++);
}
inline void ps(const std::string &s){
for(auto it:s)
if(it!='#') pc(it);
}
template<class T>
inline void read(T &s){
s=0;char ch=gc();bool f=0;
while(ch<'0'||'9'<ch) {if(ch=='-') f=1;ch=gc();}
while('0'<=ch&&ch<='9') {s=s*10+(ch^48);ch=gc();}
if(ch=='.'){
db p=0.1;ch=gc();
while('0'<=ch&&ch<='9') {s=s+p*(ch^48);p*=0.1;ch=gc();}
}
s=f?-s:s;
}
template<class T,class ...A>
inline void read(T &s,A &...a){
read(s);read(a...);
}
};
using IO::read;
using IO::gs;
using IO::ps;
const int N=1e5+3;
int n;
int a[N];
namespace Discrete{
int id[N];
inline void work(){
for(int i=1;i<=n;++i){
id[i]=i;
}
std::sort(id+1,id+1+n,[](int x,int y){
return a[x]<a[y];
});
int la=-1e9,top=0;
for(int i=1;i<=n;++i){
if(a[id[i] ]!=la) ++top;
la=a[id[i] ];
a[id[i] ]=top;
}
}
};
int head[N],nxt[N<<1];
struct Edge{
int u,v;
}to[N<<1];
int Etot=-1;
inline void link(int u,int v){
nxt[++Etot]=head[u];
head[u]=Etot;
to[Etot]={u,v};
}
inline void link1(int u,int v){
link(u,v);link(v,u);
}
struct SegTree{
int rt[N];
int tot;
#define lc(x) t[x].lc
#define rc(x) t[x].rc
struct node{
int lc,rc;
int mx;
int add;
}t[N*40];
inline void pushup(int x){
t[x].mx=std::max(t[lc(x)].mx,t[rc(x)].mx);
}
inline bool leaf(int x){
return !lc(x) and !rc(x);
}
inline void change(int x,int add){
if(!x) return;
t[x].add+=add;t[x].mx+=add;
}
inline void pushdown(int x){
if(t[x].add){
if(!lc(x) ) lc(x)=++tot;
if(!rc(x) ) rc(x)=++tot;
change(lc(x),t[x].add);
change(rc(x),t[x].add);
t[x].add=0;
}
}
void modify(int &x,int l,int r,int ql,int qr,int k){
if(!x){
x=++tot;
t[x].mx=t[x].add=0;
t[x].lc=t[x].rc=0;
}
if(ql<=l and r<=qr){
change(x,k);
return;
}
pushdown(x);
int mid=(l+r)>>1;
if(ql<=mid) modify(lc(x),l,mid,ql,qr,k);
if(mid+1<=qr) modify(rc(x),mid+1,r,ql,qr,k);
pushup(x);
}
int merge(int x,int y){
if(!x or !y) return x|y;
if(leaf(x) ) std::swap(x,y);
if(leaf(y) ){
change(x,t[y].mx);
return x;
}
pushdown(x);pushdown(y);
lc(x)=merge(lc(x),lc(y) );
rc(x)=merge(rc(x),rc(y) );
pushup(x);
return x;
}
int find(int x,int l,int r,int p){
if(leaf(x) ){
return t[x].mx;
}
pushdown(x);
int mid=(l+r)>>1;
if(p<=mid) return find(lc(x),l,mid,p);
else return find(rc(x),mid+1,r,p);
}
int pos(int x,int l,int r,int p){
if(t[x].mx<p) return r+1;
if(leaf(x) ) return l;
pushdown(x);
int mid=(l+r)>>1;
if(p<=t[lc(x)].mx) return pos(lc(x),l,mid,p);
else return pos(rc(x),mid+1,r,p);
}
}t;
int ans[N];
void dfs(int u,int f){
for(int i=head[u];~i;i=nxt[i]){
int v=to[i].v;
if(v==f) continue;
dfs(v,u);
t.rt[u]=t.merge(t.rt[u],t.rt[v]);
}
ans[u]=t.find(t.rt[u],1,n,a[u])+1;
int r=t.pos(t.rt[u],1,n,ans[u])-1;
t.modify(t.rt[u],1,n,a[u],r,1);
}
int main(){
filein(treap);fileot(treap);
read(n);
memset(head,-1,sizeof(head) );
for(int i=1;i<=n;++i){
read(a[i]);
}
for(int i=1;i<n;++i){
int u,v;
read(u,v);
++u;++v;
link1(u,v);
}
Discrete::work();
dfs(1,1);
for(int i=1;i<=n;++i){
printf("%d ",ans[i]);
}
return 0;
}

主要是看到网上的题解讲两句就结束了,很多东西只字未提。只觉得这样的题解没有意义,纯粹是浪费时间贴个代码大可不必。于是我就发了一篇详细的。

hihocoder 1193 树堆 解题报告的更多相关文章

  1. 【九度OJ】题目1176:树查找 解题报告

    [九度OJ]题目1176:树查找 解题报告 标签(空格分隔): 九度OJ http://ac.jobdu.com/problem.php?pid=1176 题目描述: 有一棵树,输出某一深度的所有节点 ...

  2. HDU 1754 线段树入门解题报告

    ---恢复内容开始--- 题意:给定区间,每个人的成绩, Q次询问,求每次询问区间中的最大值 思路:构造线段树 代码: #include<stdio.h> #include<algo ...

  3. POJ 3264 线段树入门解题报告

    题意:给n个值, Q次询问, 每次询问给定一个区间, 要求输出该区间最大最小值之差 思路:暴力的话每次询问都要遍历多次for循环一定会超时, 用线段树记录区间的信息(左边界右边界, 该区间最大值最小值 ...

  4. [NOIP2016 DAY1 T2]天天爱跑步-[差分+线段树合并][解题报告]

    [NOIP2016 DAY1 T2]天天爱跑步 题面: B[NOIP2016 DAY1]天天爱跑步 时间限制 : - MS 空间限制 : 565536 KB 评测说明 : 2s Description ...

  5. 洛谷 P3373 【模板】线段树 2 解题报告

    P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面三种操作: 1.将某区间每一个数乘上\(x\) 2.将某区间每一个数加上\(x\) 3.求出某区间每一个数的和 输入输出格式 ...

  6. 洛谷 [FJOI2014]最短路径树问题 解题报告

    [FJOI2014]最短路径树问题 题目描述 给一个包含\(n\)个点,\(m\)条边的无向连通图.从顶点\(1\)出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多 ...

  7. [jzoj 3175] 数树数 解题报告 (树链剖分)

    interlinkage: https://jzoj.net/senior/#main/show/3175 description: 给定一棵N 个节点的树,标号从1~N.每个点有一个权值.要求维护两 ...

  8. [hihocoder #1384] Genius ACM 解题报告(倍增)

    题目链接:http://hihocoder.com/problemset/problem/1384 题目大意: 给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下: 从集合 S 中取出 M ...

  9. 【九度OJ】题目1172:哈夫曼树 解题报告

    [九度OJ]题目1172:哈夫曼树 解题报告 标签(空格分隔): 九度OJ http://ac.jobdu.com/problem.php?pid=1172 题目描述: 哈夫曼树,第一行输入一个数n, ...

随机推荐

  1. 如何用vue打造一个移动端音乐播放器

    写在前面 没错,这就是慕课网上的那个vue音乐播放器,后台是某音乐播放器的线上接口扒取,虽然这类项目写的人很多,但不得不说这还是个少有的适合vue提升的好项目,做这个项目除了想写一个比较大并且功能复杂 ...

  2. 关于 video 播放的新探索

    前端同学要使用 HTML5 播放器视频,必然会使用 video 标签,不过大多数同学只是使用了较简单的功能,其实它本身拥有不凡之力有待我们发现. 首先,我们先来看下 video 最基础的用法: 使用 ...

  3. 关于#pragma 和 _pragma

    首先要明确 #pragma 和_Pragma 是什么 这两个都是出自于c/c++ 的 ,其中#pragma 是预处理指令(preProcess directive ) ,#pragma是用来向编译器传 ...

  4. JavaEE期末复习知识点总结

    JavaEE期末复习知识点总结 Java企业应用开发环境 Maven的基础概念 Maven是一个项目管理工具,可以对 Java 项目进行构建.依赖管理 Maven仓库 Maven 仓库是项目中依赖的第 ...

  5. SSM实现个人博客-day04

    项目源码免费下载:SSM实现个人博客 有问题询问vx:kht808 3.项目搭建(SSM整合) (1)创建maven工程,导入相应的依赖 <properties> <project. ...

  6. RFC 标准文档

    RFC 标准文档 什么是 RFC ? RFC(Request For Comments)意即"请求评论",包含了关于Internet的几乎所有重要的文字资料.如果你想成为网络方面的 ...

  7. LinkedList实现

    LinkedList源码实现: public class LinkedList<E> { private class Node{ public E e; public Node next; ...

  8. 1.Java基础

    1.注释 单行注释 // 多行注释 /*回车 文档注释(注解)./**回车 2.标识符 (1)Java所有组成部分都需要名字,类名,变量名以及方法名都称为标识符 (2)且标识符只能以字母.$或者_ 开 ...

  9. Sqlalchemy异步操作不完全指北

    异步SQLAlchemy SQLAlchemy作为一款通用的Python Orm工具,在最近的版本也支持了异步操作.但网上很多资料都不是很齐全,API也不是很好查询的情况下,我便有了整理一份基础文档的 ...

  10. Blazor WebAssembly 渐进式 Web 应用程序 (PWA) 使用 LocalStorage 离线处理数据

    原文链接:https://www.cnblogs.com/densen2014/p/16133343.html Window.localStorage 只读的localStorage 属性允许你访问一 ...