线段树维护单调栈
又是一道非常棒的思维题!!!
SOL:
问题转化(倒着思考)
t时刻在某点,每次可以向后走一步或留在原地,然后t减1
每个点在(T_i)消失,求最小的(t)使得所有点都可以在消失前被访问
于是惊奇地发现留在原地一定不优,会一直往前走
破环为链(2倍),对于(iin[n,2n))走到j的时间为(t-(i-j))
(jin(i-n])满足(t-(i-j)>=T_j o t_{min}=max(T_j-j)+i)
(ans=min_{iin[n,2n)}(max_{jin(i-n,i]}a_j+i),a_j=T_j-j)
用i代替i+n-1
(ans=min_{iin[1,n])}(max_{jin[i,2n]}a_j+i)+n-1)因为(a_i>a_{i+n})
若(a_j)为后缀最大值,i为j前第一个比(a_j)大的位置,(ans=a_j+i+n)
需要维护一个单调栈
线段树维护单调栈——楼房重建
每个区间记录最大值及单调栈长度,合并时左边肯定都是单调栈的内容,右边二分一个位置恰好大于左边分最大值则剩余的加入单调栈,时间复杂度(O(nlog_n^2))
(p_0,p_1,…,p_k)是栈里留下的元素
(p_{j-1}<n<=p_j)
(ans=min_{iin[1,j]}(a_{p_i}+p_{i-1})+n)
因为((n,2n])里的最大值是([1,n])里的最大值-n,所以只用维护([1,n])
记(mx[p])为区间最大值,(t[p])为区间左半到中间的最小答案
(ans=query(1,1,n,mx[1]-n))
时间复杂度(O(nlog_n^2))
#include<bits/stdc++.h>
using namespace std;
inline int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f==1?x:-x;
}
const int N=1e5+4,inf=1e9;
int n,m,op,ans,a[N],mx[N<<2],t[N<<2];
#define lc (p<<1)
#define rc (p<<1|1)
int query(int p,int l,int r,int x){
if(l==r)return mx[p]>x?x+l:inf;
int mid=l+r>>1;
return mx[rc]>x?min(t[p],query(rc,mid+1,r,x)):query(lc,l,mid,x);
}
inline void pushup(int p,int l,int r){
mx[p]=max(mx[lc],mx[rc]);
t[p]=query(lc,l,r,mx[rc]);
}
void build(int p,int l,int r){
if(l==r){
mx[p]=a[l]-l;
return;
}
int mid=l+r>>1;
build(lc,l,mid);
build(rc,mid+1,r);
pushup(p,l,mid);
}
void modify(int p,int l,int r,int x,int y){
if(l==r){
mx[p]=y-x;
return;
}
int mid=l+r>>1;
if(x<=mid)modify(lc,l,mid,x,y);
else modify(rc,mid+1,r,x,y);
pushup(p,l,mid);
}
int main(){
n=read();m=read();op=read();
for(int i=1;i<=n;i++)a[i]=read();
build(1,1,n);
ans=query(1,1,n,mx[1]-n)+n;
cout<<ans<<"
";
while(m--){
static int x,y;
x=read();y=read();
if(op){x^=ans;y^=ans;}
modify(1,1,n,x,y);
ans=query(1,1,n,mx[1]-n)+n;
cout<<ans<<"
";
}
return (0-0);
}