[CF1491H] Yuezheng Ling and Dynamic Tree

前言

据说这是 lxl 的题啊。

你这分块不是也是正解吗?

不,我这是部分分暴力。

题目

洛谷

CF

讲解

分块就完事了。

借用重链剖分的思想,考虑维护一个 \(tp_i\) 表示 \(i\) 跳出块外的位置。

散块怎么改复杂度都没问题,所以我们开整块。

对于一个整块,其大小为 \(\sqrt{n}\),所以整块被修改了至多 \(\sqrt{n}\) 次之后,里面的所有元素跳一次就可以出块,所以维护整块修改次数即可,超过 \(\sqrt{n}\) 次之后就是分块版本的区间加了,很简单。

对于询问,类似重链剖分,先跳后面的,如果它一步可以跳出块或者两个点的 \(tp\) 相同就暴力一步一步跳,否则跳 \(tp\),显然时间也是 \(O(\sqrt{n})\)

总时间复杂度 \(O(n\sqrt{n})\)

注意每次改的时候从左往右改,改散块的时候前面那个部分改了后面也要改。

前面的 \(tp\) 变了,后面的 \(tp\) 也可能跟着变,这一点非常重要,我 \(\tt \color{red}WA\) 了很久。

代码

没卡常也不需要卡常的代码
//12252024832524
#include <cmath>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 100010;
int n,Q,B;
int a[MAXN];

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int tp[MAXN],cnt[MAXN],lz[MAXN];
int ID(int x){if(x <= 1) return x-1;return (x-2)/B+1;}
int FID(int x){return B*(x-1)+2;}

int pre(int i){return Max(1,a[i]-lz[ID(i)]);}
void up(int i)
{
	if(ID(pre(i))^ID(i)) tp[i] = pre(i);
	else tp[i] = tp[pre(i)];//没出块,整个块的都tp没问题 
}
void solve(int l,int r,int x)//左闭右开! 
{
	for(int i = l;i < r;++ i)
	{
		a[i] = Max(a[i]-x,1);
		up(i);
	}
} 

int main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	n = Read(); Q = Read();
	B = ceil(sqrt(n));
	for(int i = 2;i <= n;++ i) a[i] = Read();
	for(int i = 2;i <= n;++ i) up(i);
	while(Q --> 0)
	{
		int opt = Read(),u = Read(),v = Read();
		if(opt == 1)
		{
			int U = ID(u),V = ID(v),x = Read();
			if(U+1 >= V) solve(u,v+1,x),solve(FID(U),Min(n+1,FID(V+1)),0);//散块要改后面1
			else
			{
				solve(u,FID(U+1),x);
				for(int i = U+1;i < V;++ i)
				{
					++cnt[i];
					if(cnt[i] >= B) lz[i] = Min(lz[i]+x,n);//此时tp作废 
					else solve(FID(i),FID(i+1),x);
				}
				solve(FID(V),v+1,x);
				solve(FID(V),Min(n+1,FID(V+1)),0);//散块要改后面2
			}
		}
		else
		{
			while(u^v)
			{
				if(u<v) swap(u,v);
				//一步出去  或者  在同一个块里面且出去一样
				if((ID(pre(u))^ID(u)) || (ID(u) == ID(v) && tp[u] == tp[v])) u = pre(u);
				else u = tp[u];//一步出不去,必定没觉醒,没觉醒之前,tp[u] 是对的 
			}
			Put(u,'\n');
		}
	}
	return 0;
}
posted @ 2021-10-12 17:15  皮皮刘  阅读(32)  评论(0编辑  收藏  举报