[HNOI2010]BOUNCE 弹飞绵羊
Posted Mrsrz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HNOI2010]BOUNCE 弹飞绵羊相关的知识,希望对你有一定的参考价值。
题目:BZOJ2002、洛谷P3203、codevs2333。
题目大意:有n个装置,编号0~n-1。每个装置有一个弹跳系数,第i个装置可以把羊弹到第$i+k_i$个装置上,然后继续弹。如果这个位置没有装置,则停止弹。
现在有m个操作,可以问你一只绵羊在某点开始弹,弹多少次才能结束,也可以修改一个装置的弹跳系数。
解题思路:分块。
一般都是每块分成$\sqrt{n}$个(最后一个可能不同),分成$\sqrt{n}$或$\sqrt{n}+1$块。
设jp[i]表示从这个点跳到另一个块的某个点要多少下,nxt[i]表示从i开始跳,跳到另一个块后,跳到的节点的编号。则可以发现,更新一个弹跳系数,只会变化该块内的答案(一个点可能跳到相同块内一个点)。
然后记录下每个块的起点,更新答案时一个一个更新即可(注意倒着更新)。
输出弹跳次数则更加简单,每次累计jp[i],然后令i=nxt[i]即可。
更新答案最多涉及一整个块,输出弹跳次数最多每个块有一个节点被访问,所以一次处理的时间复杂度为$O(\sqrt{n})$。
则总时间复杂度为$O(m\sqrt{n})$。
当然您也可以用LCT秒了这道题。
C++ Code:
#include<cstdio> #include<cctype> #include<cstring> #include<cmath> #define N 200020 int n,size,jp[N],nxt[N],k[N],l[505],inbk[N],blocks; inline int readint(){ char c=getchar(); for(;!isdigit(c);c=getchar()); int d=0; for(;isdigit(c);c=getchar()) d=(d<<3)+(d<<1)+(c^‘0‘); return d; } int ask(int s){ int ans=0; while(s!=-1){ ans+=jp[s]; s=nxt[s]; } return ans; } int main(){ n=readint(); size=(int)(sqrt(n)+0.00000001); blocks=0; l[0]=0; for(int i=0;i<n;++i){ k[i]=readint(); inbk[i]=blocks; if(i%size==size-1)l[++blocks]=i+1; } if(n%size)++blocks; memset(nxt,-1,sizeof nxt); for(int i=n-1;i>=0;--i){ if(i+k[i]>=n)jp[i]=1;else if(inbk[i]==inbk[i+k[i]]) jp[i]=jp[i+k[i]]+1,nxt[i]=nxt[i+k[i]];else jp[i]=1,nxt[i]=i+k[i]; } for(int m=readint();m--;){ int f=readint(); if(f==1)printf("%d\n",ask(readint()));else{ int x=readint(); k[x]=readint(); for(int i=x;i>=l[inbk[x]];--i) if(inbk[i]==inbk[i+k[i]])jp[i]=jp[i+k[i]]+1,nxt[i]=nxt[i+k[i]];else jp[i]=1,nxt[i]=i+k[i]; } } return 0; }
以上是关于[HNOI2010]BOUNCE 弹飞绵羊的主要内容,如果未能解决你的问题,请参考以下文章
bzoj 2002: [Hnoi2010]Bounce 弹飞绵羊
BZOJ2002: [Hnoi2010]Bounce 弹飞绵羊
[BZOJ2002][Hnoi2010]Bounce 弹飞绵羊