题目大意:给出一棵有根树(根为 \(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. 小程序开发之一(使用fly进行http封装)

    原文地址:http://callmesoul.cn 下载fly js文件 fly小程序文档 /api/config.js 配置,主要配置全局的host url和request拦截和request拦截 ...

  2. 前端每日实战:134# 视频演示如何用 CSS 和 GSAP 创作一个树枝发芽的 loader

    效果预览 按下右侧的"点击预览"按钮可以在当前页面预览,点击链接可以全屏预览. https://codepen.io/comehope/pen/LJmpXZ 可交互视频 此视频是可 ...

  3. react 实用项目分享-mock server

    使用react16+router4+mobx+koa2+mongodb做的mock平台 moapi-cli 本地工具版,一行命令 ,方便个人使用 安装 npm i moapi-cli -g 使用 mo ...

  4. ccf颁奖晚会

    感想: 首先,十分感谢学校给我们参加比赛的机会,给予我们这次难能可贵的学习机会,第一次在这么大型的赛场中展现自己,我们也不免有些紧张.从最开始在线上进行模型训练,到我们不远千里在江苏进行场地的勘察,到 ...

  5. 【小程序开发】 点击button按钮,引导用户授权

    一. 前言 小程序官方文档,上面说明 wx.getUserInfo(OBJECT) 注意:此接口有调整,使用该接口将不再出现授权弹窗,请使用 <button open-type="ge ...

  6. CSS样式写在JSP代码中的几种方法

    1.行内样式. 可以直接把css代码写在现有的HTML标签元素的开始标签里面,并且css样式代码要写在style=" "双引号中才可以, 如: <p style=" ...

  7. 安装scrapy速度慢解决方案

    使用终端pip安装scrapy龟速 解决方案: 使用清华源下载 清华园链接 https://mirrors.tuna.tsinghua.edu.cn/help/pypi/ win+R打开cmd 输入p ...

  8. 掌握JavaScript中的迭代器和生成器,顺便了解一下async、await的原理

    掌握JavaScript中的迭代器和生成器,顺便了解一下async.await的原理 前言 相信很多人对迭代器和生成器都不陌生,当提到async和await的原理时,大部分人可能都知道async.aw ...

  9. PyQt5 基本语法(四)

    目录 2. 输入控件(一) 2.1 纯键盘 2.1.1 QLineEdit 2.1.1.1 描述 2.1.1.2 控件创建 2.1.1.3 输出模式 2.1.1.4 提示字符串 2.1.1.5 清空按 ...

  10. 数据库基础知识详解三:MVCC、范式以及表连接方式

    写在文章前:本系列文章用于博主自己归纳复习一些基础知识,同时也分享给可能需要的人,因为水平有限,肯定存在诸多不足以及技术性错误,请大佬们及时指正. 8.MVCC 多版本并发控制(Multi-Versi ...