CF1491H Yuezheng Ling and Dynamic Tree
CF1491H Yuezheng Ling and Dynamic Tree
给定一棵大小为 \(n\) 的 \(1\) 为根节点的树,树用如下方式给出:输入 \(a_2,a_3,\dots,a_n\),保证 \(1\leq a_i<i\),将 \(a_i\) 与 \(i\) 连边形成一棵树。
接下来有两种操作:
\(1.\)1 l r x令 \(a_i=\max(a_i-x,1)(l\leq i\leq r)\)。
\(2.\)2 u v查询在当前的 \(a\) 数组构成的树上 \(u,v\) 的 LCA。
两种操作共有 \(q\) 个。
对于 \(100\%\) 的数据,满足 \(2\leq n,q \leq 10^5\),\(1 \le a_i < i\)。
首先你会发现这个题好像推不出来什么有用的性质,考虑根号数据结构。
考虑分块,将 \(a_i\) 分块,令块长为 \(B\),对于每一个块,记其左端点为 \(L_i\),由端点为 \(R_i\)。对于每一个结点 \(i\),记其所属的块编号为 \(bel_i\),然后预处理出它的祖先中编号最大的和它不在同一个块的祖先 \(b_i\),你会发现这时容易的。具体来说,考虑在 \(a_i\) 更新的时候如何重构一个块的 \(b_i\) 值。对于这个块 \(x\),我们可以从前往后枚举节点 \(i\),判断其 \(a_i\) 是否小于 \(L_x\),如果是的话则 \(b_i=a_i\),否则 \(b_i=b_{a_i}\)。
然后考虑询问操作,对于两个节点 \(x,y\),如何找到他们的 \(\texttt{LCA}\) 呢?
-
当 \(bel_x≠bel_y\) 时,让 \(bel_i\) 值大的哪个节点跳到它的 \(b_i\)。
-
当 \(bel_x=bel_y\) 且 \(b_x≠b_y\) 时,两个节点均跳到它们的 \(b_i\)。
-
当 \(bel_x=bel_y\) 且 \(b_x=b_y\) 时,直接让编号大的那个节点向自己的父亲跳,直至两点相同即可。
综上所述,查询的总时间复杂度为 \(O(q\sqrt{n})\)。
接下来考虑修改操作。
对于零散点的修改时容易的,只需暴力修改其 \(a_i\) 值,然后重构整个块的 \(b_i\) 值即可。
你发现对于整块的修改要向平衡时间复杂度很困难。然后我们进行一波观察。对于每一个块,它经过 \(\sqrt{n}\) 次修改操作后 \(a_i\) 都至少变为 \(a_i-\sqrt{n}\),此时节点 \(i\) 的所有祖先都一定在这一块之前,于是此时的 \(b_i\) 值都不变,且为 \(a_i\)。
因此我们可以维护每一块被修改的次数 \(cnt_i\)。当 \(cnt_i>\sqrt {n}\) 时就直接忽略,无需修改了,分析一下复杂度,每块最多修改 \(\sqrt {n}\) 次,时间复杂度 \(O(n\sqrt{n})\)。
点击查看代码
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
#define M 355
int n,m,B,a[N];
int bel[N],L[N],R[N];
int fa[N],pre[N];
int cnt[M],tag[M];
int read ()
{
int k=1,s=0;char ch=getchar ();
while (!isdigit (ch)) {if (ch=='-') k=-1;ch=getchar ();}
while (isdigit (ch)) {s=s*10+ch-'0';ch=getchar ();}
return k*s;
}
void Recover (int x)
{
for (int i=L[x];i<=R[x];i++)
fa[i]=max (1,fa[i]-tag[x]);
tag[x]=0;
for (int i=L[x];i<=R[x];i++)
if (fa[i]<L[x]) pre[i]=fa[i];
else pre[i]=pre[fa[i]];
}
void Modify (int l,int r,int val)
{
int x=bel[l],y=bel[r];
if (x==y)
{
for (int i=l;i<=r;i++)
fa[i]=max (1,fa[i]-val);
Recover (x);
return;
}
for (int i=l;i<=R[x];i++)
fa[i]=max (1,fa[i]-val);
for (int i=L[y];i<=r;i++)
fa[i]=max (1,fa[i]-val);
Recover (x),Recover (y);
for (int i=x+1;i<y;i++)
{
cnt[i]++;
tag[i]=min (N,tag[i]+val);
if (cnt[i]<=B) Recover (i);
}
}
int GetPre (int x)
{
return max (1,pre[x]-tag[bel[x]]);
}
int GetLCA (int x,int y)
{
while (bel[x]!=bel[y] || GetPre (x)!=GetPre (y))
{
if (bel[x]<bel[y]) swap (x,y);
if (bel[x]!=bel[y]) x=GetPre (x);
else x=GetPre (x),y=GetPre (y);
}
while (x!=y)
{
if (x<y) swap (x,y);
x=max (1,fa[x]-tag[bel[x]]);
}
return x;
}
void Init ()
{
n=read (),m=read ();
B=sqrt (n);
for (int i=2;i<=n;i++)
fa[i]=read ();
}
void Work ()
{
for (int i=1;i<=n;i++)
{
bel[i]=(i-1)/B+1;
if (!L[bel[i]]) L[bel[i]]=i;
R[bel[i]]=i;
}
for (int i=1;i<=bel[n];i++)
Recover (i);
while (m--)
{
int op=read (),x=read (),y=read ();
if (op==1)
{
int w=read ();
Modify (x,y,w);
}
else printf ("%d\n",GetLCA (x,y));
}
}
signed main ()
{
Init ();
Work ();
return 0;
}

浙公网安备 33010602011771号