bzoj2002 弹飞绵羊 分块orLCT

       先简单讲一下LCT做法,每个点以它的到达点为父节点(没有到达点就连向根),结果就是深度。然后修改就是改变父亲,一个cut再一个link就好了。

       然后是分块。每个点维护它到达它所在块外面的点,及步数,预处理O(N)。查询时O(N/m)。修改时只需要修改x所在块在x前面的那些点即可,时间O(m)。因此总时间复杂度O(M*(N/m+m)+N)=O(MN^0.5+N)(取m=N^0.5)

AC代码如下:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#define N 200005
using namespace std;

int n,m,a[N],nxt[N],len[N],blg[N],l[5005],r[5005];
int read(){
	int x=0; char ch=getchar();
	while (ch<'0' || ch>'9') ch=getchar();
	while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
	return x;
}
int main(){
	n=read(); m=(int)sqrt(n); int i,j;
	for (i=1; i<=n; i++) a[i]=read();
	for (i=1; i<=m; i++){
		l[i]=r[i-1]+1; r[i]=r[i-1]+m;
	}
	r[m]=n;
	for (i=1; i<=m; i++)
		for (j=l[i]; j<=r[i]; j++) blg[j]=i;
	for (i=n; i; i--){
		int x=i+a[i];
		if (x>n){ nxt[i]=0; len[i]=1; }
		else if (x>r[blg[i]]){ nxt[i]=x; len[i]=1; }
		else{ nxt[i]=nxt[x]; len[i]=len[x]+1; }
	}
	int cas=read();
	while (cas--){
		int k=read(),x=read()+1;
		if (k==1){
			int ans=0; for (i=x; i; i=nxt[i]) ans+=len[i];
			printf("%d\n",ans);
		} else{
			int y=read(),t=blg[x]; a[x]=y;
			for (i=x; i>=l[t]; i--){
				int tmp=i+a[i];
				if (tmp>n){ nxt[i]=0; len[i]=1; }
				else if (tmp>r[t]){ nxt[i]=tmp; len[i]=1; }
				else{ nxt[i]=nxt[tmp]; len[i]=len[tmp]+1; }
			}
		}
	}
	return 0;
}

by lych
2016.2.23

你可能感兴趣的:(分块,LCT)