【HNOI2010】弹飞绵羊
问题描述
某天,Lostmonkey发明了一种超级弹力装置,为了在他的绵羊朋友面前显摆,他邀请小绵羊一起玩个游戏。游戏一开始,Lostmonkey在地上沿着一条直线摆上n个装置,每个装置设定初始弹力系数ki,当绵羊达到第i个装置时,它会往后弹ki步,达到第i+ki个装置,若不存在第i+ki个装置,则绵羊被弹飞。绵羊想知道当它从第i个装置起步时,被弹几次后会被弹飞。为了使得游戏更有趣,Lostmonkey可以修改某个弹力装置的弹力系数,任何时候弹力系数均为正整数。
输入格式
第一行包含一个整数n,表示地上有n个装置,装置的编号从0到n-1,
接下来一行有n个正整数,依次为那n个装置的初始弹力系数。
第三行有一个正整数m,接下来m行每行至少有两个数i、j,若i=1,你要输出从j出发被弹几次后被弹飞,若i=2则还会再输入一个正整数k,表示第j个弹力装置的系数被修改成k。
对于20%的数据n,m<=10000,对于100%的数据n<=200000,m<=100000
输出格式
对于每个i=1的情况,你都要输出一个需要的步数,占一行。
样例输入
4
1 2 1 1
3
1 1
2 1 1
1 1
样例输出
2
3
当时我并不会LCT。但是这道题可以用分块做。
按照分块的思想,我们想知道某个位置跳出当前块需要的步数,这样就可以大块大块地跳,最多跳 n√ 次。为了配套,同时也要处理出跳出当前块后走到的第一个位置。这样就可以让查询操作的时间复杂度控制在 O(n√) 级别。
考虑如何处理出这两个数组。为了表示方便,记从i位置跳出它所在的块需要的步数为 step[i] ,跳出它所在块走到的第一个位置为 nex[i] 。
首先根据题目可知,一个位置的弹力系数改变后,答案会受到影响的部分只在它的左边。所以应该从右往左讨论。一开始每个位置跳到的下一个位置是 i+ki 。如果 i+ki 已经超出了当前的区间,那么 nex[i] 就是 i+ki , step[i] 就是1。否则 nex[i]=nex[i+ki] , step[i]=step[i+ki]+1 。
修改操作的处理方法是相同的。那么这道题就解决了,时间复杂度 O(nn√) 。
#include<stdio.h>
#include<cmath>
#define MAXN 200005
int N,M,S,be[MAXN],A[MAXN],L[MAXN],R[MAXN],step[MAXN],nex[MAXN];
void Modify(int x)
{
int i,tmp;
for(i=x;i>=L[be[x]];i--)
{
tmp=A[i]+i;
if(tmp>N)tmp=N+1;
if(be[tmp]==be[i])step[i]=step[tmp]+1,nex[i]=nex[tmp];
else step[i]=1,nex[i]=tmp;
}
}
int GetAns(int x)
{
int Ans=0;
while(x<=N)
{
Ans+=step[x];
x=nex[x];
}
return Ans;
}
int main()
{
int i,j,op,x,y,tmp;
scanf("%d",&N);
for(i=1;i<=N;i++)scanf("%d",&A[i]);
S=sqrt(N);
for(i=j=1,L[1]=1;i<=N;i++)
{
be[i]=j;
if(i%S==0)R[j]=i,L[++j]=i+1;
}
R[be[N]]=N;
nex[N+1]=N+1;
for(j=1;j<=be[N];j++)
for(i=R[j];i>=L[j];i--)
{
tmp=A[i]+i;
if(tmp>N)tmp=N+1;
if(be[tmp]==be[i])step[i]=step[tmp]+1,nex[i]=nex[tmp];
else step[i]=1,nex[i]=tmp;
}
scanf("%d",&M);
for(i=1;i<=M;i++)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d",&x);x++;
printf("%d\n",GetAns(x));
}
else
{
scanf("%d%d",&x,&y);x++;
A[x]=y;Modify(x);
}
}
}
现在会LCT了,说说LCT的做法。
添加一个虚拟节点
n+1
表示坐标超过
n
。如果把
对于修改操作,就是先 Cut ,后 Link 。
#include<stdio.h>
#include<algorithm>
#define MAXN 200005
using namespace std;
int N,M,A[MAXN];
int ls[MAXN],rs[MAXN],fa[MAXN],Size[MAXN],rev[MAXN];
bool isrt(int x){return ls[fa[x]]!=x&&rs[fa[x]]!=x;}
void Putdown(int p)
{
if(rev[p]==0)return;
rev[p]=0;rev[ls[p]]^=1;rev[rs[p]]^=1;
swap(ls[p],rs[p]);
}
void Update(int p){Size[p]=Size[ls[p]]+Size[rs[p]]+1;}
void Zig(int x)
{
int y=fa[x],z=fa[y];
if(!isrt(y))
{
if(ls[z]==y)ls[z]=x;
else rs[z]=x;
}
fa[x]=z;fa[y]=x;fa[rs[x]]=y;
ls[y]=rs[x];rs[x]=y;
Update(y);Update(x);
}
void Zag(int x)
{
int y=fa[x],z=fa[y];
if(!isrt(y))
{
if(ls[z]==y)ls[z]=x;
else rs[z]=x;
}
fa[x]=z;fa[y]=x;fa[ls[x]]=y;
rs[y]=ls[x];ls[x]=y;
Update(y);Update(x);
}
int s[MAXN],Top;
void Splay(int x)
{
int i,y,z;
s[++Top]=x;
for(i=x;!isrt(i);i=fa[i])s[++Top]=fa[i];
while(Top)Putdown(s[Top--]);
while(!isrt(x))
{
y=fa[x];z=fa[y];
if(isrt(y))
{
if(ls[y]==x)Zig(x);
else Zag(x);
}
else
{
if(ls[z]==y)
{
if(ls[y]==x)Zig(y),Zig(x);
else Zag(x),Zig(x);
}
else
{
if(rs[y]==x)Zag(y),Zag(x);
else Zig(x),Zag(x);
}
}
}
}
void Access(int x)
{
int t=0;
while(x)
{
Splay(x);
rs[x]=t;Update(x);
t=x;x=fa[x];
}
}
void Setrt(int x)
{
Access(x);Splay(x);
rev[x]^=1;
}
void Link(int x,int y)
{
Setrt(x);fa[x]=y;
}
void Cut(int x,int y)
{
Setrt(x);
Access(y);Splay(y);
ls[y]=fa[x]=0;
Update(y);
}
int GetSum(int x)
{
Setrt(N+1);
Access(x);
Splay(x);
return Size[x]-1;
}
int main()
{
int i,x,y,op,k;
scanf("%d",&N);
for(i=1;i<=N;i++)
{
scanf("%d",&x);
A[i]=x;y=i+x;
if(y>N+1)y=N+1;
Link(i,y);
}
for(i=1;i<=N+1;i++)Size[i]=1;
scanf("%d",&M);
while(M--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d",&x);x++;
printf("%d\n",GetSum(x));
}
else
{
scanf("%d%d",&x,&k);
x++;
y=A[x]+x;
if(y>N+1)y=N+1;
Cut(x,y);
y=x+k;
if(y>N+1)y=N+1;
Link(x,y);
A[x]=k;
}
}
}