Codeforces 1491H. Yuezheng Ling and Dynamic Tree 题解
题目链接:H. Yuezheng Ling and Dynamic Tree
题目大意:给定一棵大小为 \(n\) 的一 \(1\) 为根节点的树,树用如下方式给出:输入 \(a_2,a_3,\dots,a_n\),保证 \(1\leq a_i<i\),将 \(a_i\) 与 \(i\) 连边形成一棵树。
接下来有两种操作:
1 l r x
令 \(a_i=\max(a_i-x,1)(l\leq i\leq r)\)。2 u v
查询在当前的 \(a\) 数组构成的树上 \(u,v\) 的 LCA。
两种操作总数一共有 \(q\) 个。
\(2\leq n,q\leq 10^5\),\(2\leq l\leq r\leq n\),\(1\leq x\leq 10^5\),\(1\leq u,v\leq n\)
题解:将 \(a\) 序列分块,假设块长为 \(B\),对于每一个节点,预处理出它的祖先中编号最大的和它不再同一个块的祖先,为了方便,在下文中我们称其为当前点的前驱,我们先考虑如何求两个点的 LCA。
分两种情况考虑:
- \(u,v\) 不属于同一个块:将属于编号较大的一个块的节点跳至它的前驱。
- \(u,v\) 属于同一个块:接下来还是要分两种情况:
- \(u\) 的前驱和 \(v\) 的前驱不同:将 \(u,v\) 同时跳至各自的前驱。
- \(u\) 的前驱和 \(v\) 的前驱相同:此时可以轮流跳 \(u,v\) 中编号较大的点的父亲直至两个点相等。
接下来分析查询的时间复杂度:因为前驱最多不超过 \(\frac{n}{B}\) 个,而暴力跳父亲不超过 \(B\) 次,所以时间复杂度是 \(O(B+\frac{n}{B})\)。
然后我们再来考虑修改:按照序列分块的惯例,我们分整块和散块来考虑,对于散块,直接暴力修改然后暴力重构前驱即可。
但是对于整块,我们需要打懒惰标记,但是这种时候无法维护前驱的修改,因为修改一个块的前驱是需要遍历整块的。
但是我们发现,一个块最多在经过 \(B\) 次修改之后,块中每一个数的父亲都会在这个块之前,也就是说,我们对于一个块需要暴力重构前驱的次数最多是 \(B\) 次,之后的可以直接通过懒标记直接减来解决。
那么分析修改的复杂度,对于散块修改是 \(O(B)\) 的,对于整块修改(不考虑重构前驱)是 \(O(\frac{n}{B})\) 的,对于重构前驱的总时间复杂度是 \(O(nB)\) 的。
综上,若取 \(B=\sqrt{n}\),则可以获得 \(O(n\sqrt{n}+q\sqrt{n})\) 的时间复杂度,可以通过本题。
代码:
#include <cstdio>
#include <algorithm>
const int Maxb=300;
const int Maxn=100000;
const int Maxv=(Maxn-1)/Maxb+1;
int n,q;
int num[Maxv+5],lazy[Maxv+5];
int fa[Maxn+5],out[Maxn+5];
int find_belong(int x){
return (x-1)/Maxb+1;
}
int find_bel_l(int x){
return (x-1)*Maxb+1;
}
int find_bel_r(int x){
return std::min(n,x*Maxb);
}
void build(int x){
for(int i=find_bel_l(x);i<=find_bel_r(x);i++){
fa[i]=std::max(1,fa[i]-lazy[x]);
}
lazy[x]=0;
for(int i=find_bel_l(x);i<=find_bel_r(x);i++){
if(fa[i]<find_bel_l(x)){
out[i]=fa[i];
}
else{
out[i]=out[fa[i]];
}
}
}
int find_out(int x){
return std::max(1,out[x]-lazy[find_belong(x)]);
}
int find_fa(int x){
return std::max(1,fa[x]-lazy[find_belong(x)]);
}
void update(int l,int r,int x){
int bel_l=find_belong(l),bel_r=find_belong(r);
if(bel_l==bel_r){
for(int i=l;i<=r;i++){
fa[i]=std::max(1,fa[i]-x);
}
build(bel_l);
return;
}
for(int i=l;i<=find_bel_r(bel_l);i++){
fa[i]=std::max(1,fa[i]-x);
}
build(bel_l);
for(int i=find_bel_l(bel_r);i<=r;i++){
fa[i]=std::max(1,fa[i]-x);
}
build(bel_r);
for(int i=bel_l+1;i<bel_r;i++){
num[i]++;
lazy[i]=std::min(n,lazy[i]+x);
if(num[i]<=Maxb){
build(i);
}
}
}
int query(int u,int v){
while(u!=v){
if(find_belong(u)<find_belong(v)){
std::swap(u,v);
}
if(find_belong(u)>find_belong(v)){
u=find_out(u);
}
else{
if(find_out(u)!=find_out(v)){
u=find_out(u);
v=find_out(v);
}
else{
while(u!=v){
if(u<v){
std::swap(u,v);
}
u=find_fa(u);
}
}
}
}
return u;
}
int main(){
scanf("%d%d",&n,&q);
for(int i=2;i<=n;i++){
scanf("%d",&fa[i]);
}
for(int i=1;i<=find_belong(n);i++){
build(i);
}
for(int i=1;i<=q;i++){
int op;
scanf("%d",&op);
if(op==1){
int l,r,x;
scanf("%d%d%d",&l,&r,&x);
update(l,r,x);
}
else{
int u,v;
scanf("%d%d",&u,&v);
printf("%d\n",query(u,v));
}
}
return 0;
}
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。