考场上写出了70分,现在填个坑
比较好写的70分是这样的:(我考场上写的贼复杂)
设\(L(i)=\min_{j=i}^nl(j)\)
那么从i开始向左走第一步能到达的就是\([l(i),i-1]\)(显然)
第二步能到达的是\([L(l(i)),l(i)-1]\)
为什么呢,因为i一开始可以直接向左,也可以先向右走到\(l\)最小的位置然后向左,这时能跳到的区间就是\([L(i),i]\)
如果\(L(l(i))=L(i)\)显然这是一种方案,但是有可能\(L(l(i))<L(i)\),既然小了肯定是\(l(k)(k\in[l(i),i))\)这一部分\(<L(i)\),那么当然也是可行的
后面就是\([L(L(\cdots L(l(i))\cdots)),上一个的左端点]\)了,一直推下去。
#include#define il inline#define vd voidtypedef long long ll;il int gi(){ int x=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')f=-1; ch=getchar(); } while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f;}int l[300010],L[300010];ll D[5010][5010];//D[i][j]表示从i开始到[j,i-1]的跳的次数之和int main(){ int n=gi(); for(int i=2;i<=n;++i)l[i]=gi(); L[n]=l[n];for(int i=n-1;i>1;--i)L[i]=std::min(l[i],L[i+1]); for(int i=2;i<=n;++i){ int x=l[i],lst=i; for(int j=x;j
优化很显然是倍增,很容易想到\(f[i][j]\)设为\(i\)位置向前跳\(L(i)\)跳\(2^j\)次。
再来一个\(g[i][j]\)设为\(i\)位置向前跳\(2^j\)次,这中间每个位置跳的次数的和。随便理解一下就行了。
#include#define il inline#define vd voidtypedef long long ll;il int gi(){ int x=0,f=1; char ch=getchar(); while(!isdigit(ch)){ if(ch=='-')f=-1; ch=getchar(); } while(isdigit(ch))x=x*10+ch-'0',ch=getchar(); return x*f;}int l[300010],L[300010];int f[19][300010];ll g[19][300010];il ll solve(int x,int y){ if(l[x]<=y)return x-y; ll ret=x-l[x];int i,j; x=l[x]; for(i=18,j=1;~i;--i) if(f[i][x]>=y){ ret+=g[i][x]+j*(x-f[i][x]); j+=1< 1;--i)L[i]=std::min(l[i],L[i+1]); for(int i=1;i<=n;++i)f[0][i]=L[i],g[0][i]=i-L[i]; for(int i=1;i<19;++i) for(int j=1;j<=n;++j){ f[i][j]=f[i-1][f[i-1][j]]; g[i][j]=g[i-1][j]+g[i-1][f[i-1][j]]+(1<