大江东去,浪淘尽,千古风流人物。故垒西边,人道是,三国周郎赤壁。乱石穿空,惊涛拍岸,卷起千堆雪。江山如画,一时多少豪杰。遥想公瑾当年,小乔初嫁了,雄姿英发。羽扇纶巾,谈笑间,樯橹灰飞烟灭。故国神游,多情应笑我,早生华发。人生如梦,一尊还酹江月。

洛谷 P3203 [HNOI2010]弹飞绵羊

分块

\(O(n\sqrt{n})\)

也可以LCT,不过此题分块是很精妙的,值得学习

如果直接维护从i出发跳出去需要多少步,修改x时就需要修改x之前的所有点

但如果维护从i出发跳出块需要多少步,则块外的不会跳到x上,不受影响,只用修改块内的点,单次修改复杂度降至\(\sqrt{n}\)

询问时只用跳块,故也是\(\sqrt{n}\)

#include<bits/stdc++.h>
using namespace std;

#define go(i,a,b) for(int i=a;i<=b;++i)
#define com(i,a,b) for(int i=a;i>=b;--i)
#define mem(a,b) memset(a,b,sizeof(a))

const int N=200000+10,inf=0x3f3f3f3f,M=1010;

int n,K[N],L[M],R[M],pos[N],cost[N],nxt[N];

inline void read(int &x){
	x=0;char f=1,c=getchar();
	while(!isdigit(c)){ if(c=='-') f=-1; c=getchar(); }
	while(isdigit(c)){ x=x*10+c-'0'; c=getchar(); }
	x*=f;
}

int cal(int x){
	int ans=0;
	while(x){
		ans+=cost[x];
		x=nxt[x];
	}
	return ans;
}

int main(){
	//freopen("input.txt","r",stdin);
	read(n);
	go(i,1,n) read(K[i]);
	int t=sqrt(n);
	go(i,1,t){
		L[i]=R[i-1]+1;
		R[i]=i*t;
	}
	if(R[t]<n){
		L[++t]=R[t-1]+1;
		R[t]=n;
	}
	go(i,1,t)
		go(j,L[i],R[i])
			pos[j]=i;
	com(i,n,1){
		if(i+K[i]>n) cost[i]=1;
		else if(pos[i]==pos[i+K[i]]){
			cost[i]=cost[i+K[i]]+1;
			nxt[i]=nxt[i+K[i]];
		}
		else cost[i]=1,nxt[i]=i+K[i];
	}
	int op,x,k;
	int m;read(m);
	go(i,1,m){
		read(op);read(x);++x;
		if(op==1) printf("%d\n",cal(x));
		else{
			read(k);
			K[x]=k;
			com(j,x,L[pos[x]]){
				if(pos[j]==pos[j+K[j]]){
					cost[j]=cost[j+K[j]]+1;
					nxt[j]=nxt[j+K[j]];
				}
				else cost[j]=1,nxt[j]=j+K[j];
			}
		}
	}
	return 0;
}
posted @ 2019-09-17 20:20  White_star  阅读(116)  评论(0编辑  收藏  举报
}