传送门:bzoj5286
题解
考虑是个环,把
数组双倍复制一下,得到:
证明:
当 时,显找到最小的差 后在起点等待这么久后,就可以畅通无阻地走完一圈,而答案是最优的。
当
时,从某一起点出发,有可能先走完一圈其他点后再绕回这个最大点,答案最优,而如果按照上面的式子算,显然不是最优的(这也是问题所在,如下图(出发点为蓝点,红点
))。
考虑在走完一圈后,红蓝之间的点显然是多走了一次的,而这些时间应该包含在等待
中,而不是额外再算,但完全可以从绿点出发,对于绿点来说,先走和后走就没什么区别了,上式也就满足了最优。
故对于所有情况,都存在上式可以取到最优解。
再观察枚举起点后的取答案:
实际上等价于
,因为显然
,这样化简后等价于求一个后缀最大,处理出
,则
。
没有修改的询问直接
查询即可。
有修改的询问用线段树优化。
考虑线段树如何优化单调队列:
线段树节点
(管辖范围
)上存两个信息:
。
(线段树根节点范围为
,查询直接回答
)。
考虑如何 :
设当前节点为 (管辖区间为 ),左儿子为 (管辖区间为 ),右儿子为 (管辖区间为 )。
对于 ,由 可得 ,而 ,通过 递归得到(底层为 ),分类讨论一下 :
时, 不变,只需要修改 ,返回
时, 整个都会统一增大,但不确定 与 的关系,返回
复杂度
代码
#include<bits/stdc++.h>
#define gc getchar
#define si isdigit
#define mid (((l)+(r))>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=2e5+100;
int n,m,a[N],op,ans;
int mx[N<<2],val[N<<2];
char c;
inline int rd()
{
c=gc();int x=0;
for(;!si(c);c=gc());
for(;si(c);c=gc()) x=x*10+(c^48);
return x;
}
inline int get(int ql,int mxx,int l,int r)
{
if(l==r) return max(mx[ql],mxx)+l;
if(mx[ql<<1|1]>=mxx) return min(val[ql],get(ql<<1|1,mxx,mid+1,r));
return min(mid+1+mxx,get(ql<<1,mxx,l,mid));
}
inline void update(int k,int l,int r)
{
mx[k]=max(mx[lc],mx[rc]);
val[k]=get(lc,mx[rc],l,mid);
}
inline void build(int k,int l,int r)
{
if(l==r){val[k]=a[l]+l;mx[k]=a[l];return;}
build(lc,l,mid);build(rc,mid+1,r);
update(k,l,r);
}
inline void change(int k,int l,int r,int pos)
{
if(l==r) {val[k]=a[l]+l;mx[k]=a[l];return;}
if(pos<=mid) change(lc,l,mid,pos);
else change(rc,mid+1,r,pos);
update(k,l,r);
}
inline void out(int x){if(x>9) out(x/10);putchar('0'+x%10);}
int main(){
int i,j,x,y;
n=rd();m=rd();op=rd();
for(i=1;i<=n;++i){a[i]=rd()-i;a[i+n]=a[i]-n;}
build(1,1,n<<1);
out((ans=val[1]+n-1));puts("");
for(;m;--m){
x=rd();y=rd();
if(op) x^=ans,y^=ans;
a[x]=y-x;a[x+n]=y-x-n;
change(1,1,n<<1,x);change(1,1,n<<1,x+n);
out((ans=val[1]+n-1));puts("");
}
}