Luogu4425 , BZOJ5286 , LOJ2495
一些题解,以及暂时看不了的
相关题目:[P4198楼房重建] [[HNOI2017]影魔]
可以发现转化之后你留在原地一定是不优的 , 也就是说你会一直往前走 , 其实区别在于开始的时间
比如原来的最优解为\(a=\{ 1,2,3,6,7 \}\) 可以转化为 \(a=\{3,4,5,6,7\}\)
枚举出发时间\(t\)和出发点\(i\) , 枚举等待时间最长的点\(j\) , 则有\[ans=min_i\{max\{T_j-(j-i)\}+n-1\}\]
设\(x_i=T_i-i\) , 即\[ans=min_i\{max\{x_j\}+i\}+n-1\]
又有\(x_j>x_{j+n}\) , 所以\(x_{j+n}\)对枚举最大值不会有影响 , 线段树上询问的范围也就可以从\([i,i+n-1]\)换为\([i,2n]\)
线段树维护单调栈结构 , 记\(mx[i] = max\{T_j-j \} , ans[i] = min\{max\{T_j-j\}+i\}\)
非常好的题 , 代码也很值得推敲 , 非常值得借鉴
具体细节见代码
维护单调栈的信息时维护整个区间非常不好搞 , 可以只维护一半的信息 , 另一半通过动态的比较得出
往左边继续找的时候 , 要记得比一下右边的答案 ; 往右边继续找的时候 , 要记得比一下左边的答案 ;
注释更新于\(19.3.31\)
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define debug(...) fprintf(stderr,__VA_ARGS__)
#define Debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
typedef long long LL;
const int INF=1e9+7;
inline LL read(){
register LL x=0,f=1;register char c=getchar();
while(c<48||c>57){if(c=='-')f=-1;c=getchar();}
while(c>=48&&c<=57)x=(x<<3)+(x<<1)+(c&15),c=getchar();
return f*x;
}
const int MAXN=2e5+5;
int a[MAXN];
int n,m,type,lastans;
struct SGT{
// ans[i] = min{ max{ Tj-j } + i } 定义为在左儿子中的答案
// mx[i] = max{ Tj-j }
//这里懒得卡什么空间之类的,枚举范围直接到2n,方便想,反正也不会错
int ans[MAXN<<2],mx[MAXN<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
//要求min{max{Xj}+i},所以i要尽可能小,所以维护一半信息的话就应该是把右边的参数传到左边
//还是当做模板记下来吧...
inline int query(int rt,int l,int r,int tmp){//得到上一层区间左半边的答案
//核心:用到tmp或者把tmp继续往下传
if(l==r) return l+max(mx[rt],tmp);//特判长度为1:必须根据定义
int mid=(l+r)>>1;
//直接返回答案也变成了直接传一半的参到另一半
if(mx[rs]>=tmp) return min(ans[rt],query(rs,mid+1,r,tmp));//右边的mx更大,可能答案在右边,但也要记得比较左边的答案ans[rt]
else return min(query(ls,l,mid,tmp),tmp+mid+1);//应该往左边找,而右边的答案是tmp+mid+1(已经判过tmp>mx[rs])
}
inline void pushup(int rt,int l,int r,int mid){
//维护最值的写法:直接把一半的参传到另一半去比较,维护某个半边的答案,这样才能做到另半边能够动态的比较得出
ans[rt]=query(ls,l,mid,mx[rs]);
mx[rt]=max(mx[ls],mx[rs]);//mx[]很好更新
}
inline void build(int rt,int l,int r){
if(l==r){
ans[rt]=a[l];
mx[rt]=a[l]-l;
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
pushup(rt,l,r,mid);//非常骚的线段树维护单调栈结构的pushup()
}
inline void modify(int rt,int l,int r,int x){
if(l==r){
ans[rt]=a[l];
mx[rt]=a[l]-l;
return;
}
int mid=(l+r)>>1;
if(x<=mid) modify(ls,l,mid,x);
else modify(rs,mid+1,r,x);
pushup(rt,l,r,mid);
}
#undef ls
#undef rs
}T;
int main(){
n=read(),m=read(),type=read();
for(int i=1;i<=n;i++) a[i]=a[i+n]=read();
T.build(1,1,n<<1);
printf("%d\n",lastans=T.ans[1]+n-1);
for(int i=1;i<=m;i++){
int x=read(),y=read();
if(type) x^=lastans,y^=lastans;
a[x]=a[x+n]=y;
T.modify(1,1,n<<1,x);
T.modify(1,1,n<<1,x+n);
printf("%d\n",lastans=T.ans[1]+n-1);
}
}
\(19.3.21\)注释