hihocoder 1193 树堆 解题报告
题目大意:给出一棵有根树(根为 \(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 树堆 解题报告的更多相关文章
- 【九度OJ】题目1176:树查找 解题报告
[九度OJ]题目1176:树查找 解题报告 标签(空格分隔): 九度OJ http://ac.jobdu.com/problem.php?pid=1176 题目描述: 有一棵树,输出某一深度的所有节点 ...
- HDU 1754 线段树入门解题报告
---恢复内容开始--- 题意:给定区间,每个人的成绩, Q次询问,求每次询问区间中的最大值 思路:构造线段树 代码: #include<stdio.h> #include<algo ...
- POJ 3264 线段树入门解题报告
题意:给n个值, Q次询问, 每次询问给定一个区间, 要求输出该区间最大最小值之差 思路:暴力的话每次询问都要遍历多次for循环一定会超时, 用线段树记录区间的信息(左边界右边界, 该区间最大值最小值 ...
- [NOIP2016 DAY1 T2]天天爱跑步-[差分+线段树合并][解题报告]
[NOIP2016 DAY1 T2]天天爱跑步 题面: B[NOIP2016 DAY1]天天爱跑步 时间限制 : - MS 空间限制 : 565536 KB 评测说明 : 2s Description ...
- 洛谷 P3373 【模板】线段树 2 解题报告
P3373 [模板]线段树 2 题目描述 如题,已知一个数列,你需要进行下面三种操作: 1.将某区间每一个数乘上\(x\) 2.将某区间每一个数加上\(x\) 3.求出某区间每一个数的和 输入输出格式 ...
- 洛谷 [FJOI2014]最短路径树问题 解题报告
[FJOI2014]最短路径树问题 题目描述 给一个包含\(n\)个点,\(m\)条边的无向连通图.从顶点\(1\)出发,往其余所有点分别走一次并返回. 往某一个点走时,选择总长度最短的路径走.若有多 ...
- [jzoj 3175] 数树数 解题报告 (树链剖分)
interlinkage: https://jzoj.net/senior/#main/show/3175 description: 给定一棵N 个节点的树,标号从1~N.每个点有一个权值.要求维护两 ...
- [hihocoder #1384] Genius ACM 解题报告(倍增)
题目链接:http://hihocoder.com/problemset/problem/1384 题目大意: 给定一个整数 M,对于任意一个整数集合 S,定义“校验值”如下: 从集合 S 中取出 M ...
- 【九度OJ】题目1172:哈夫曼树 解题报告
[九度OJ]题目1172:哈夫曼树 解题报告 标签(空格分隔): 九度OJ http://ac.jobdu.com/problem.php?pid=1172 题目描述: 哈夫曼树,第一行输入一个数n, ...
随机推荐
- CSS揭秘之《多重边框》
1.box-shadow还接受第四个参数(称作"扩张半径"), 通过指定正值或负值, 可以让投影面积加大或者减小2.如果我们想要一道实线边框其实也是可以通过box-shadow来模 ...
- 人机交互BS
B/S结构用户界面设计 [实验编号] 10003809548j Web界面设计 [实验学时] 8学时 [实验环境] l 所需硬件环境为微机: l 所需软件环境为dreamweaver ...
- CCF201912-2 回收站选址
解题思路:这道题唬人的在于坐标有正有负哈,刚开始不知道怎么下爪,仔细思考过后,我可是会面向对象编程的啊哈哈哈哈,我可是最喜欢封装了哈哈哈哈. 1.首先可以把每个点用一个结构体来定义,包含他的x,y坐标 ...
- HTML 和 form 表单常用标签
HTML和CSS 常用标签: p:段落,自动换行 span:和div类似,但是默认不换行 br:换行 hr:分割线 h1-h6:标题标签 a:超链接 瞄点:通过给a链接设置#XX作为链接,给需要链接的 ...
- Map集合的六种遍历方式
学习目标: 熟练掌握Map的遍历方式 例题: 需求:遍历Map集合 代码如下: package com.yy.object.test.test_collection; import java.util ...
- getHibernateTemplate出现的所有find方法的总结
文章转自:http://www.cnblogs.com/DarrenChan/p/5528194.html 一.find(String queryString); 示例:this.getHiberna ...
- 华为交换机Stelnet ssh/rsa验证模式下16进制公钥生成方法
1.生成秘钥 需要在你自己电脑上生成 执行下面命令,默认生成位置是~/.ssh ssh-keygen -t rsa -b 1024 -f yourkeyname -C "备注" 参 ...
- AWS - Basic 1
之前由于公司 Training 考取了 AWS-SAP 的证书,更多理解的是概念和理论上的知识,并未实操.但对于学习一门技术来说,实践是加深理解和掌握该技术的必经之路,强调知行合一.所以最近打算重新熟 ...
- Ant Design Pro V5 与 IdentityServer 实现 Password 模式的登录
最近处于休息状态,想趁着休息时间,为自己做一个后台. 后端框架选用了 Abp.之前公司使用了一些自研的框架,但由于人力资源有限,后期框架的升级及维护都是比较耗时,这次干脆直接使用Abp,即省心又能快速 ...
- 用js实现倒计时效果
首先获得两个时间的时间戳 var newdate = new Date('2021-01-22 21:25:00').getTime(); var olddate = new Date().getTi ...