bzoj 5286: [Hnoi2018]转盘

Description

Solution

首先注意到一个点不会走两次,只会有停下来等待的情况,把序列倍长
那么如果枚举一个起点\(i\),答案就是 \(min(max(T[j]+n-(j-i)-1)),j∈[i,2*n]\)
相当于从 \(i\) 出发,先走到 \(j\) 停下来,然后再走完剩下的,如果不合法则不会更优
最优情况一定是把等待时间尽量用在前面(把起点往前移)

\(a[i]=T[i]-i\)
原式变为: \(min(max(a[j]+i)+n-1),j∈[i,2*n]\)
维护 \(max(a[j]+i)\) 即可,可以用线段树维护,每一次修改向上合并维护这个东西

\(i,j\) 是有偏序关系的每一次要维护跨过 \(mid\) 的答案
向上合并需要一个 \(log\) 的递归查询
复杂度是 \(O(n*log^2)\)

#include<bits/stdc++.h>
#define ls (o<<1)
#define rs (o<<1|1)
using namespace std;
const int N=2e5+10;
int n,tr[N*4],mx[N*4],Q,P,T[N],a[N],ans=0;
inline int qry(int l,int r,int o,int x){
	if(l==r)return l+max(x,mx[o]);
	int mid=(l+r)>>1;
	if(mx[rs]>=x)return min(tr[o],qry(mid+1,r,rs,x));
	return min(qry(l,mid,ls,x),mid+1+x);
}
inline void upd(int l,int r,int o){
	int mid=(l+r)>>1;
	tr[o]=qry(l,mid,ls,mx[rs]);
	mx[o]=max(mx[ls],mx[rs]);
}
inline void build(int l,int r,int o){
	if(l==r){tr[o]=T[l];mx[o]=a[l];return ;}
	int mid=(l+r)>>1;
	build(l,mid,ls);build(mid+1,r,rs);
	upd(l,r,o);
}
inline void Modify(int l,int r,int o,int sa){
	if(l==r){tr[o]=T[l];mx[o]=a[l];return ;}
	int mid=(l+r)>>1;
	if(sa<=mid)Modify(l,mid,ls,sa);
	else Modify(mid+1,r,rs,sa);
	upd(l,r,o);
}
int main(){
  freopen("circle.in","r",stdin);
  freopen("circle.out","w",stdout);
  cin>>n>>Q>>P;
  for(int i=1;i<=n;i++){
	  scanf("%d",&T[i]);T[i+n]=T[i];
	  a[i]=T[i]-i;a[i+n]=T[i+n]-i-n;
  }
  build(1,n*2,1);
  printf("%d\n",ans=tr[1]+n-1);
  int x,y;
  while(Q--){
	  scanf("%d%d",&x,&y);x^=ans*P;y^=ans*P;
	  T[x]=y;T[x+n]=y;a[x]=T[x]-x;a[x+n]=T[x+n]-x-n;
	  Modify(1,n*2,1,x);Modify(1,n*2,1,x+n);
	  printf("%d\n",ans=tr[1]+n-1);
  }
  return 0;
}

posted @ 2018-04-18 20:29  PIPIBoss  阅读(914)  评论(9编辑  收藏  举报