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;
}
posted @ 2023-12-26 13:33  Cyber_Punk  阅读(48)  评论(0)    收藏  举报