【AtCoder Regular Contest 077 E】guruguru
差分。
题目大意:
给定一个环和一个数列 $a_1,a_2,a_3,\ldots,a_n$。你要从 $a_1$ 开始,顺时针行走,依次到达 $a_2,a_3,\ldots,a_n$。走一个单位花费 $1$ 的代价。
你可以确定一个位置为关键点,然后不管在什么地方,都可以花费 $1$ 的代价走到关键点处。
问总代价最小是多少。
题解:
我们考虑 $x$ 到 $y$ 的路径上,在 $x+2,x+3,x+4,\cdots$ 位置上放关键点并跳过去,会节约 $1,2,3,\cdots$ 步。
一个点能够节约的总步数,就是所有包含这个点的路径中,直接跳到这个点时能节约的步数。
我们考虑计算出每个点能节约的步数。对于一条路径,它会给一个区间加上一个等差数列。我们要在最后的时候查询所有值。所以只需要用二阶差分维护就好了。
至于环上的差分,我们可以倍长之后断环为链,然后在链上差分即可。
时间复杂度 $O(n+m)$。
Code:
#include<cstdio>
int n,m,d[114514];
long long ans,A[233333];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i)
scanf("%d",d+i);
for(int i=1;i<n;++i){
int l=d[i],r=d[i+1];
if(l>r)r+=m;
ans+=r-l;
++A[l+2],--A[r+1];
A[r+1]-=r-l-1,A[r+2]+=r-l-1;
}
for(int i=1;i<=m*2;++i)A[i]+=A[i-1];
for(int i=1;i<=m*2;++i)A[i]+=A[i-1];
for(int i=1;i<=m;++i)A[i]+=A[i+m];
long long mx=0;
for(int i=1;i<=m;++i)if(mx<A[i])mx=A[i];
printf("%lld\n",ans-mx);
return 0;
}