Top Tree 树上邻域数点
EnofTaiPeople · · 题解
Part1 前言
题意需要我们维护一棵静态树,
这道题太神了,以致于在 NOI2022 出现后被加入 Ynoi2003,这里感谢 zx2003,让我发现之前学的点权和
Part2 \text{Top Tree} 维护边集的静态构建
这道题的本质是
上图是一棵树,加粗的点表示重儿子,它所对应的
其中
Part3 对簇内信息的记录
簇其实就是连通的边集,并且有两个端点,上图的
如何维护簇内信息?
记
对于一个
对于每一个节点,维护一个
ln[x]=ln[ls],U[x]=U[ls],V[x]=V[ls];
for(i=0;i<=rz[x];++i){
g[x][0][i]=R(G(ls,0,i),G(rs,0,i));
g[x][1][i]=R(G(ls,1,i),G(rs,0,i-ln[x]));
}
对于一个
ln[x]=ln[ls]+ln[rs],U[x]=U[ls],V[x]=V[rs];
for(i=0;i<=rz[x];++i){
g[x][0][i]=C(G(ls,0,i),G(rs,0,i-ln[ls]));
g[x][1][i]=C(G(ls,1,i-ln[rs]),G(rs,1,i));
}
由于每一个节点只会保留边数的信息,而此树具有全局平衡的性质,所以时空复杂度
Part4 对簇外信息的记录
发现对于一个节点,如果不是根簇端点,可能会很尴尬:从叶子往上跳,跳多了得到的信息就超了,跳少了信息又不完全,我们无法做到让查询的信息总在一个簇内与簇的某一端点完全相邻,于是需要记录簇外的信息。
记
无论是哪一个节点,它的簇外信息都有两部分组成:父亲的簇外信息和兄弟的簇内信息,这方面不难推导,依旧只需要对于两种类型的节点分类讨论就可以了:
if(tp[x]){
h[ls][0].resize(rz[x]+1);
h[ls][1]=h[rs][0]=h[ls][0];
h[rs][1]={emptyinfo};
for(i=0;i<=rz[x];++i)
h[ls][1][i]=H(x,1,i);
for(i=0;i<=rz[x];++i){
h[ls][0][i]=R(G(rs,0,i),H(x,0,i));
h[rs][0][i]=R(C(G(ls,0,i),H(ls,1,i-ln[ls])),H(x,0,i));
}
}else{
h[ls][0].resize(rz[x]+1);
h[rs][0]=h[ls][1]=h[rs][1]=h[ls][0];
for(i=0;i<=rz[x];++i){
h[ls][0][i]=H(x,0,i);
h[rs][1][i]=H(x,1,i);
h[ls][1][i]=C(H(x,1,i-ln[rs]),G(rs,0,i));
h[rs][0][i]=C(H(x,0,i-ln[ls]),G(ls,1,i));
}
}
大家可能发现了,对于每一个节点的簇外信息,我们都将其保留到了父亲的大小,这样可以方便查询。
具体地,从查询节点开始跳,直到父亲的簇大小超过
容易发现,这一部分必定会完全包含簇内信息,所以在预处理时将
Part5 代码效率说明
首先,这是我的第一次提交记录,也推荐大家参考,因为没有删注释,但是只有 too many MR,MC operations in init(),然后就莫名其妙地 Hack 成功了,后来发现这是被卡常了,微调了一下建树过程,就通过了,或许是出题人并不想卡人?反正目前是最优最短解,指不定哪一天就被人挤下去了。
然而,这样的做法又被我自己 Hack 掉了,原因是,当数据为一棵完全二叉树时,高度会达到
Part6 后记
不知道自己想干什么,学一道题目花了一个星期,或许这是我联赛之前的最后一次任性吧。但这道题真的十分有教育意义,将树上问题在链和子树的基础上,又向全新的“邻域”问题发展,也让我了解到了