[HNOI/AHOI2018] 转盘

首先可以发现一定不会停下,因为把停下的时间转化为开头往前挪一步不会使得其他物品的限制变紧

考虑在最后一次经过某个物品时取这个物品,那么枚举终点进行一个时光倒流,断环为链后相当于从 \([n+1,2n]\) 的某个位置出发,一直往前走,使得经过物品 \(i\) 的时间 \(\ge T_i\)

设终点为 \(n+p\),结束时间为 \(t\),那么限制即 \(\forall i,t-(n+p-i)\ge T_i\),也就是 \(t=\max\limits_{i=1}^n(T_i-i+n+p)\),分离一下得到 \(t=\max\limits_{i=1}^n(T_i-i)+n+p\)

但是这个式子其实并不正确,因为断环为链之后前缀 \(p\) 到达的时间要加上一个 \(n\),所以说这个前缀内的最大值对 \(t\) 的贡献要减去 \(n\)

注意到如果序列中的最大值 \(mx\) 不在前缀中,那么 \(p\) 几乎没有影响,如果在前缀中,答案就是 \(\max(mx,\max\limits_{i=p+1}^n(T_i-i)+n)+p\),若要更小就要把后面的最大值也包括到前缀中,那么一个物品能贡献到答案当且仅当其是后缀最大值,可以想到线段树维护单调栈,对所有后缀最大值算出最小的答案,复杂度 \(O(n\log^2n)\),代码不到 1k

点击查看代码
#include<bits/stdc++.h>
#define inf (int)2e9
#define N 100005
using namespace std;
int read(){
	int w=0,h=1;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-h;ch=getchar();}
	while(ch>='0'&&ch<='9'){w=w*10+ch-'0';ch=getchar();}
	return w*h;
}
int n,m,p,ans;
namespace SGT{
#define ls k<<1
#define rs k<<1|1
#define mid ((l+r)>>1)
	int Max[N<<2],Min[N<<2];
	int Bound(int k,int l,int r,int x){
		if(l==r)return Max[k]>x?x+l:inf;
		return Max[rs]>x?min(Min[k],Bound(rs,mid+1,r,x)):Bound(ls,l,mid,x);
	}
	void Modify(int k,int l,int r,int x,int y){
		if(l==r)return void(Max[k]=y-x);
		x<=mid?Modify(ls,l,mid,x,y):Modify(rs,mid+1,r,x,y);
		Max[k]=max(Max[ls],Max[rs]);Min[k]=Bound(ls,l,mid,Max[rs]);
	}
}
using namespace SGT;
signed main(){
	n=read();m=read();p=read();
	for(int i=1;i<=n;i++)Modify(1,1,n,i,read());
	printf("%d\n",ans=Bound(1,1,n,Max[1]-n)+n);
	while(m--){
		int x=read()^(p*ans),y=read()^(p*ans);
		Modify(1,1,n,x,y);
		printf("%d\n",ans=Bound(1,1,n,Max[1]-n)+n);
	}
	return 0;
}
posted @ 2023-11-02 15:38  pidan007  阅读(12)  评论(0编辑  收藏  举报