• 【洛谷7446】[Ynoi2007] rfplca(分块)


    题目链接

    • 有一棵 \(n\) 个点的以 \(1\) 为根的有根树,其中 \(i\) 号点的初始父节点为 \(a_i\)\(1\le a_i < i\))。
    • \(q\) 次操作,分为两种:将 \([l,r]\) 中所有 \(a_i\) 修改为 \(\max\{a_i-x,1\}\);询问 \(\operatorname{LCA}(x,y)\)
    • \(2\le n,q\le4\times10^5\)\(1\le x\le4\times10^5\),强制在线

    分块求 \(\operatorname{LCA}\)

    我们首先要解决的问题是如何利用分块求出 \(\operatorname{LCA}\)

    \(a_i\)\(i\) 的父节点,\(f_i\)\(i\) 首个不在当前块的祖先。然后进行一系列讨论:

    • 如果当前 \(x,y\) 不同块:让后面那个块的点跳 \(f\)
    • 如果当前 \(x,y\) 同块,但 \(f_x\not=f_y\):如果 \(x,y\)\(\operatorname{LCA}\) 在这个块内则首个不在当前块的祖先必然相同,也就是说 \(x,y\)\(\operatorname{LCA}\) 不在当前块中,让 \(x,y\) 同时跳 \(f\)
    • 如果当前 \(x,y\) 同块,且 \(f_x\not=f_y\):说明 \(x,y\)\(\operatorname{LCA}\) 在这个块中或就是 \(f_x\),可以不断让后面那个点往前跳 \(a\) 直至两点相同。

    假设块大小为 \(\sqrt n\)。前两种情况因为跳 \(f\) 导致块编号减小,复杂度 \(O(\sqrt n)\);最后一种情况因为只会在一个块内跳,复杂度也是 \(O(\sqrt n)\)

    分块维护修改

    我们可以不在修改时考虑向 \(1\)\(\max\) 的操作,而是在询问时比较,这样一来修改操作就变成了简单的区间减法。

    然后发现如果 \(a_i\) 本来就不在块中,\(f_i=a_i\),和 \(a_i\) 一样减少 \(x\)

    如果 \(a_i\) 在块中,由于至多减去 \(\sqrt n\)\(a_i\) 就不可能在块中,也就是至多被操作 \(\sqrt n\) 次后就会被归到上一种情况。

    所以对于整块修改可以直接打标记,并特殊维护出 \(a_i\) 在块内的节点暴力修改。

    散块直接暴力改后重构即可。

    代码:\(O(n\sqrt n)\)

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Rg register
    #define RI Rg int
    #define Cn const
    #define CI Cn int&
    #define I inline
    #define W while
    #define N 400000
    #define LL long long
    using namespace std;
    int n;LL a[N+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');}
    }using namespace FastIO;
    namespace Block
    {
    	#define BS 320
    	#define BT 1280
    	int sz,bl[N+5],p[BT+5],q[BT+5];LL f[N+5],tg[BT+5];vector<int> g[BT+5];
    	queue<int> tmp[BS+5];I void Bd(CI o)//重构块
    	{
    		RI i,x,l=(o-1)*sz+1,r=min(o*sz,n);for(i=l;i<=r;++i) (a[i]-=tg[o])>=l?(tmp[a[i]-l].push(i),0):f[i]=a[i];tg[o]=0;//传下标记,a在块内的节点扔入对应桶
    		for(p[o]=q[o]=0,g[o].clear(),i=l;i<=r;++i) W(!tmp[i-l].empty()) ++q[o],g[o].push_back(x=tmp[i-l].front()),tmp[i-l].pop(),f[x]=f[i];//按顺序维护a在块内的节点
    	}
    	I void BF(CI l,CI r,CI v)//散块
    	{
    		for(RI i=l;i<=r;++i) a[i]-=v;Bd(bl[l]);//暴力修改后重构
    	}
    	I void Tg(CI o,CI v)//整块
    	{
    		RI l=(o-1)*sz+1;tg[o]+=v;W(p[o]^q[o]&&a[g[o][p[o]]]-tg[o]<l) f[g[o][p[o]]]=a[g[o][p[o]]],++p[o];//整块打标记;a变得不在块中的特殊点,归为一般情况
    		for(RI i=p[o];i^q[o];++i) f[g[o][i]]=f[a[g[o][i]]-tg[o]];//a仍在块中的特殊点,暴力修改
    	}
    	I void U(CI l,CI r,CI v)//分块修改
    	{
    		RI L=bl[l],R=bl[r];if(L==R) return BF(l,r,v);BF(l,L*sz,v),BF((R-1)*sz+1,r,v);for(RI i=L+1;i<R;++i) Tg(i,v);
    	}
    	I int Q(RI x,RI y)//询问LCA
    	{
    		#define F(x) max(f[x]-tg[bl[x]],1LL)
    		RI t;W(bl[x]^bl[y]||F(x)^F(y)) x<y&&(swap(x,y),0),bl[x]^bl[y]?x=F(x):(x=F(x),y=F(y));//不在同一块中或LCA不在当前块中
    		W(x^y) if(x<y&&(swap(x,y),0),(t=max(a[x]-tg[bl[x]],1LL))==F(x)) return t;else x=t;return x;//确定在当前块中或就是f[x],不断往前跳
    	}
    }using namespace Block;
    int main()
    {
    	RI Qt,i,op,l,r,x;for(read(n,Qt),sz=sqrt(n)/2,bl[1]=1,i=2;i<=n;++i) read(a[i]),bl[i]=(i-1)/sz+1;for(i=1;i<=bl[n];++i) Bd(i);
    	RI t=0;W(Qt--) read(op,l,r),l^=t,r^=t,op==1?(read(x),U(l,r,x^t)):writeln(t=Q(l,r));return clear(),0;
    }
    
  • 相关阅读:
    那些年做过的外包 之 健康小屋
    Raize 重新编译
    解码淘口令?
    单品优惠券炸了
    如何共享联盟cookie
    阿里妈妈账号登录状态如何长时间保存
    delphi 图像旋转
    Canvas: Out of system resources
    关于&$地址传递的练习
    解决XAMPP中,MYSQL因修改my.ini后,无法启动的问题
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu7446.html
Copyright © 2020-2023  润新知