BZOJ传送门
洛谷传送门
解析:
首先,这道题的题意仍然不是很好理解。简要叙述如下:
一个 n n n元环,在 0 0 0时刻,你可以选择环上任意一个点站上去,之后每一个时间点,你可以选择走到下一个位置或者停留不动。
环上第 i i i个点在 T i T_i Ti时刻会启动,如果你当前所在的点启动了,你可以选择将其标记,标记一个点不消耗时间,问标记所有点需要的最短时间。 T i T_i Ti带修改,强制在线。
我们考虑这个过程的逆过程。让时光逆流。
我们当前在点 i i i,时刻为 t t t,只能停留或倒退,第 i i i个点必须在时间倒流回 T i T_i Ti之前访问。
显然可以发现,停留这个选项毫无意义。
我们唯一的选择是前进。
现在考虑 i i i确定的时候,怎样的 t i t_i ti可能满足答案。
首先用老套路断环为链,长度翻倍。令 T i + n = T i T_{i+n}=T_i Ti+n=Ti
则为了走到所有节点,我们有 t i + ( j − i ) ≥ T j min ( t i ) = max ( T j − j ) + i t_i+(j-i)\geq T_j\\\min(t_i)=\max(T_j-j) +i ti+(j−i)≥Tjmin(ti)=max(Tj−j)+i
而显然答案就是: A n s = min i = n + 1 2 n ( max j = i − n i ( T j − j ) + i ) Ans=\min_{i=n+1}^{2n}(\max_{j=i-n}^i(T_j-j)+i) Ans=i=n+1min2n(j=i−nmaxi(Tj−j)+i)
令 a i = T i − i a_i=T_i-i ai=Ti−i,则 a i > a i + n a_i > a_{i+n} ai>ai+n
将 i i i的范围改为 [ 1 , n ] [1,n] [1,n]我们有如下转化:
A n s = min i = 1 n ( max j = i i + n ( a j ) + i ) + n − 1 = min i = 1 n ( max j = i 2 n ( a j ) + i ) + n − 1 Ans=\min_{i=1}^n(\max_{j=i}^{i+n}(a_j)+i)+n-1\\=\min_{i=1}^n(\max_{j=i}^{2n}(a_j)+i)+n-1 Ans=i=1minn(j=imaxi+n(aj)+i)+n−1=i=1minn(j=imax2n(aj)+i)+n−1
现在考虑一个 a j a_j aj对答案的影响。
如果 a j a_j aj是后缀最大值,则找到它前面第一个比它大的 a i a_i ai,它的答案就是 a j + i + 1 a_j+i+1 aj+i+1
如果 a j a_j aj不是后缀最大值,那么它的答案和当前后缀最大值一样。
所以我们需要维护一个单调栈。
直接上线段树维护就行了,关于线段树维护单调栈,可以看看这道题:https://www.luogu.org/problemnew/show/P4198
发现答案其实就是 [ n + 1 , 2 n ] [n+1,2n] [n+1,2n]的最大值在 [ 1 , n ] [1,n] [1,n]里面二分找到的,于是可以只维护 [ 1 , n ] [1,n] [1,n]的单调栈和最大值,显然 [ n + 1 , 2 n ] [n+1,2n] [n+1,2n]的最大值就是 [ 1 , n ] [1,n] [1,n]的最大值 − n -n −n。
代码:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<20|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
inline int getint(){
re char c;
while(!isdigit(c=gc()));re int num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return num;
}
}
using namespace IO;
cs int N=1e5+5,INF=0x3f3f3f3f;
int n,m,p,lasans;
int mx[N<<2],ans[N<<2];
inline int query(int k,int l,int r,cs int &val){
if(l==r)return mx[k]>val?val+l:INF;
int mid=(l+r)>>1;
return mx[k<<1|1]>val?min(ans[k],query(k<<1|1,mid+1,r,val)):query(k<<1,l,mid,val);
}
inline void pushup(cs int &k,cs int &l,cs int &r){
mx[k]=max(mx[k<<1],mx[k<<1|1]);
ans[k]=query(k<<1,l,(l+r)>>1,mx[k<<1|1]);
}
inline void build(int k,int l,int r){
if(l==r){
mx[k]=getint()-l;
return ;
}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
pushup(k,l,r);
}
inline void modify(int k,int l,int r,int pos,int val){
if(l==r){
mx[k]=val-l;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid)modify(k<<1,l,mid,pos,val);
else modify(k<<1|1,mid+1,r,pos,val);
pushup(k,l,r);
}
signed main(){
n=getint(),m=getint(),p=getint();
build(1,1,n);
cout<<(lasans=query(1,1,n,mx[1]-n)+n)<<"\n";
while(m--){
int x=getint()^(p*lasans),y=getint()^(p*lasans);
modify(1,1,n,x,y);
cout<<(lasans=query(1,1,n,mx[1]-n)+n)<<"\n";
}
return 0;
}