望先生不吝赐教
800 年之前加到主题库里面的 Ynoi。突然意识到哦原来这是个 Global Round 的题目。刚出来的时候挼了好久都没搞出来,看了眼做法没去补……结果今天在赛时突然遇见了,然后就写出来了。当然这是题外话。
这个题显然不可能维护出树的形态,也不好用
联想到树链剖分求 LCA。在树链剖分求 LCA 的做法中,我们优化时间复杂度的方法是,构造「重链」这样一个概念,优化了在重儿子中往上跳的复杂度。将序列分块,我们需要构造一个方法,使得跳 LCA 的时间复杂度是
- 如果两个点块外的最近祖先不在同一个块内:两个点中块外的最近祖先编号大的那个跳向其块外最近祖先;
- 否则,如果两个点块外的最近祖先不是同一个点:两个点中块外的最近祖先编号大的那个跳向其块外最近祖先;
- 否则:因为是块外最近祖先,我们暴力跳也能保证复杂度,编号大的那个点往其父亲跳即可。
现在的话,我们需要维护的东西有一个点块外的最近祖先和其父亲结点。考虑维护方法。
注意到在一个块修改了很多次之后,这个块内的父亲节点和块外最近祖先其实是一个东西。发现这个值最大是这个块的长度,即
如果一个块被修改了多次,我们就可以直接打懒标记了。最后查询块外最近祖先和父亲的时候,先判断一下是否存在标记,如果有标记就说明父亲已经到块外去了,直接跳就行。具体可以看代码。
#include<bits/stdc++.h>
using namespace std;
int read()
{
int x=0;
char c=getchar();
while(c<'0' || c>'9') c=getchar();
while(c>='0' && c<='9') x=(x<<1)+(x<<3)+(c^'0'),c=getchar();
return x;
}
void write(int x)
{
if(x>=10) write(x/10);
putchar(x%10+'0');
}
const int MAXN=100010;
int n,T,brk,bel[MAXN],L[505],R[505],tag[505],sum[505],fa[MAXN],top[MAXN];
int getAcOut(int x){return sum[bel[x]]>brk?max(1,fa[x]-tag[bel[x]]):top[x];}
int getAcIn(int x){return sum[bel[x]]>brk?max(1,fa[x]-tag[bel[x]]):fa[x];}
void modify(int x){if(sum[bel[x]]<=brk) top[x]=(bel[fa[x]]!=bel[x])?fa[x]:top[fa[x]];}
void modify(int x,int val)
{
if(sum[x]>brk) tag[x]=min(tag[x]+val,n);
else for(int i=L[x];i<=R[x];++i) fa[i]=max(fa[i]-val,1),modify(i);
}
void modify(int lp,int rp,int val)
{
int l=bel[lp],r=bel[rp];
if(l==r)
{
for(int i=lp;i<=rp;++i) fa[i]=max(fa[i]-val,1);
for(int i=lp;i<=R[r];++i) modify(i);
return ;
}
for(int i=lp;i<=R[l];++i) fa[i]=max(fa[i]-val,1),modify(i);
for(int i=L[r];i<=rp;++i) fa[i]=max(fa[i]-val,1);
for(int i=L[r];i<=R[r];++i) modify(i);
for(int i=l+1;i<=r-1;++i) modify(i,val),++sum[i];
}
int main(){
n=read(),T=read();
brk=sqrt(n);
for(int i=2;i<=n;++i) fa[i]=read();
fa[1]=top[1]=1;
for(int i=1;i<=n;++i) bel[i]=(i-1)/brk+1;
for(int i=1;i<=n;++i)
{
if(!L[bel[i]]) L[bel[i]]=i;
R[bel[i]]=i;
}
for(int i=2;i<=n;++i) modify(i);
while(T-->0)
{
int op=read();
if(op==1)
{
int l=read(),r=read(),x=read();
modify(l,r,x);
}
else
{
int u=read(),v=read();
while(u!=v)
{
int acu=getAcOut(u),acv=getAcOut(v);
if(bel[acu]!=bel[acv])
{
if(bel[acu]<bel[acv]) v=acv;
else u=acu;
}
else if(acu!=acv)
{
if(acu<acv) v=acv;
else u=acu;
}
else
{
if(u<v) v=getAcIn(v);
else u=getAcIn(u);
}
}
write(u),puts("");
}
}
return 0;
}