一个结论:一定存在一个最优解只走一圈。否则考虑从最后一个结束位置开始一定可以达到相同效果
画个图,类似是一种斜线感觉
考虑一个高度贡献的最高点
对于i开始的连续n个,答案是:max(Tj-j)+i+n-1
令ai=Ti-i
断环成链复制一倍,后面的ai只能更小,所以变成后缀:max(aj)+i+n-1
求ans=min(max(aj)+i+n-1)
还是不行
我们反过来考虑一个j会贡献的最小的i
如果一个j是整个后缀部分的最大值,那么贡献的最小的i就是j前面第一个大于aj的位置
如果不是,那么没用。
第一个性质就是单调栈模型了
线段树维护单调栈
ans=min(a(p(j))-p(j-1)),注意p(0)=0,并且必须p(j-1)<n
合并时候:
右儿子最大值小于c,不管,递归左儿子
大于c,记录le表示该区间,右儿子最大值开始往左整个前缀贡献的ans,然后取一下,递归右儿子
回来时候把当前区间的le更新
看代码更直观
代码:
#include<bits/stdc++.h> #define reg register int #define il inline #define numb (ch^'0') #define mid ((l+r)>>1) using namespace std; typedef long long ll; il void rd(int &x){ char ch;x=0;bool fl=false; while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true); for(x=numb;isdigit(ch=getchar());x=x*10+numb); (fl==true)&&(x=-x); } namespace Miracle{ const int N=200000+5; const int inf=0x3f3f3f3f; int n,m,typ; int a[N]; struct node{ int ans,mx;//mx->-0x3f3f3f3f int le; }t[4*N]; int pushup(int x,int l,int r,int c){ if(l==r){ if(t[x].mx<=c&&l-1<n) return c+l-1; else if(t[x].mx>c&&l<n) return c+l; return inf; } if(t[x<<1|1].mx<=c) return pushup(x<<1,l,mid,c); return min(t[x].le,pushup(x<<1|1,mid+1,r,c)); } void build(int x,int l,int r){ if(l==r){ t[x].mx=a[l]; if(l-1<n) t[x].ans=a[l]+l-1,t[x].le=l-1; else t[x].ans=inf,t[x].le=inf; return; } build(x<<1,l,mid); build(x<<1|1,mid+1,r); t[x].mx=max(t[x<<1].mx,t[x<<1|1].mx); t[x].ans=min(t[x].le=pushup(x<<1,l,mid,t[x<<1|1].mx),t[x<<1|1].ans); //cout<<" after pushup "<<l<<" "<<r<<" ans "<<t[x].ans<<" le "<<t[x<<1|1].le<<endl; } void chan(int x,int l,int r,int p,int c){ if(l==r){ t[x].mx=a[l]; if(l-1<n) t[x].ans=a[l]+l-1,t[x].le=l-1; else t[x].ans=inf,t[x].le=inf; return; } if(p<=mid) chan(x<<1,l,mid,p,c); else chan(x<<1|1,mid+1,r,p,c); t[x].mx=max(t[x<<1].mx,t[x<<1|1].mx); t[x].ans=min(t[x].le=pushup(x<<1,l,mid,t[x<<1|1].mx),t[x<<1|1].ans); } int main(){ rd(n);rd(m);rd(typ); for(reg i=1;i<=n;++i){ rd(a[i]);a[i]=a[i]-i; a[i+n]=a[i]-n; } build(1,1,2*n); printf("%d ",t[1].ans+n); int lasans=t[1].ans+n; int x,y; while(m--){ rd(x);rd(y); if(typ) x^=lasans,y^=lasans; a[x]=y-x; a[x+n]=y-x-n; chan(1,1,2*n,x,y-x); chan(1,1,2*n,x+n,y-x); printf("%d ",lasans=t[1].ans+n); } return 0; } } signed main(){ Miracle::main(); return 0; } /* Author: *Miracle* Date: 2019/3/2 15:07:28 */
思路:
发现走一圈性质
断环为链复制一倍,找到i开始的时间,区间max,变成后缀max
反过来考虑一个点什么时候可能成为贡献ans的一员,单调栈的性质。线段树维护单调栈
跨区间pushup时候,保留左儿子和跨区间的信息,额外记录le变量
转化成后缀比区间好一些,考虑后缀max就可以套路地变成贡献了