P3203 [HNOI2010]弹飞绵羊

时间:2023-03-09 09:08:29
P3203 [HNOI2010]弹飞绵羊

LCT裸题,之后填坑打一下

分块做法:每个点存几次出块以及出块的位置,问的时候直接暴力跳就vans了

首先思考最普通的模拟,发现可以O(n)路径压缩,O(1)的查询,但是需要修改就变成了O(n^2)的修改,于是考虑分块,记录一下每个点跳出该点所在的块的步数,也就是在每块内进行路径压缩,还有记录每个点跳出块后到达的点,同样可以块内路径压缩完成,这样就变成了O(sqrt(n))的修改和查询,但是预处理是O(n*sqrt(n))的,虽然可以过,但是LCT更快

时间复杂度:O((m+n)sqrt(n))

#include<bits/stdc++.h>using namespace std;
const int N=2e5+7;
int n,m,pos[N],a[N],times[N],end[N],L[N],R[N];
void calc(int l,int r){
	for(int i=r;i>=l;i--){
		if(i+a[i]>=R[pos[i]]){
			times[i]=1;
			end[i]=i+a[i];
		}
		else {
			times[i]=times[i+a[i]]+1;
			end[i]=end[i+a[i]];
		}
	}
}
int main(){
	cin>>n;
	int t=sqrt(n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		a[i];
		pos[i]=(i-1)/t+1;
	}
	cin>>m;
	for(int i=1;i<=t;i++){
		L[i]=(i-1)*t+1;
		R[i]=i*t;
	}
	if(R[t]<n){
		t++;
		L[t]=R[t-1]+1;
		R[t]=n;
	}
	calc(1,n);
	for(int i=1;i<=m;i++){
		int x,y,z;
		scanf("%d%d",&x,&y);
		y++;
		if(x==1){
			int ans=0;
			while(y<=n){
				ans+=times[y];
				y=end[y];
			}
			printf("%d\n",ans);
		}
		else {
			scanf("%d",&z);
			a[y]=z;
			calc(L[pos[y]],R[pos[y]]);
		}
	}
	return 0;
}