2019.03.01【HNOI2018/AHOI2018】【BZOJ5286】【洛谷P4425】转盘(线段树维护单调栈)

BZOJ传送门

洛谷传送门


解析:

首先,这道题的题意仍然不是很好理解。简要叙述如下:

一个 n n n元环,在 0 0 0时刻,你可以选择环上任意一个点站上去,之后每一个时间点,你可以选择走到下一个位置或者停留不动。

环上第 i i i个点在 T i T_i Ti时刻会启动,如果你当前所在的点启动了,你可以选择将其标记,标记一个点不消耗时间,问标记所有点需要的最短时间。 T i T_i Ti带修改,强制在线。

我们考虑这个过程的逆过程。让时光逆流。

我们当前在点 i i i,时刻为 t t t,只能停留或倒退,第 i i i个点必须在时间倒流回 T i T_i Ti之前访问。

显然可以发现,停留这个选项毫无意义。

我们唯一的选择是前进。

现在考虑 i i i确定的时候,怎样的 t i t_i ti可能满足答案。

首先用老套路断环为链,长度翻倍。令 T i + n = T i T_{i+n}=T_i Ti+n=Ti

则为了走到所有节点,我们有 t i + ( j − i ) ≥ T j min ⁡ ( t i ) = max ⁡ ( T j − j ) + i t_i+(j-i)\geq T_j\\\min(t_i)=\max(T_j-j) +i ti+(ji)Tjmin(ti)=max(Tjj)+i

而显然答案就是: A n s = min ⁡ i = n + 1 2 n ( max ⁡ j = i − n i ( T j − j ) + i ) Ans=\min_{i=n+1}^{2n}(\max_{j=i-n}^i(T_j-j)+i) Ans=i=n+1min2n(j=inmaxi(Tjj)+i)

a i = T i − i a_i=T_i-i ai=Tii,则 a i > a i + n a_i > a_{i+n} ai>ai+n

i i i的范围改为 [ 1 , n ] [1,n] [1,n]我们有如下转化:

A n s = min ⁡ i = 1 n ( max ⁡ j = i i + n ( a j ) + i ) + n − 1 = min ⁡ i = 1 n ( max ⁡ j = i 2 n ( a j ) + i ) + n − 1 Ans=\min_{i=1}^n(\max_{j=i}^{i+n}(a_j)+i)+n-1\\=\min_{i=1}^n(\max_{j=i}^{2n}(a_j)+i)+n-1 Ans=i=1minn(j=imaxi+n(aj)+i)+n1=i=1minn(j=imax2n(aj)+i)+n1

现在考虑一个 a j a_j aj对答案的影响。

如果 a j a_j aj是后缀最大值,则找到它前面第一个比它大的 a i a_i ai,它的答案就是 a j + i + 1 a_j+i+1 aj+i+1

如果 a j a_j aj不是后缀最大值,那么它的答案和当前后缀最大值一样。

所以我们需要维护一个单调栈。

直接上线段树维护就行了,关于线段树维护单调栈,可以看看这道题:https://www.luogu.org/problemnew/show/P4198

发现答案其实就是 [ n + 1 , 2 n ] [n+1,2n] [n+1,2n]的最大值在 [ 1 , n ] [1,n] [1,n]里面二分找到的,于是可以只维护 [ 1 , n ] [1,n] [1,n]的单调栈和最大值,显然 [ n + 1 , 2 n ] [n+1,2n] [n+1,2n]的最大值就是 [ 1 , n ] [1,n] [1,n]的最大值 − n -n n


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int N=1e5+5,INF=0x3f3f3f3f;
int n,m,p,lasans;
int mx[N<<2],ans[N<<2];

inline int query(int k,int l,int r,cs int &val){
	if(l==r)return mx[k]>val?val+l:INF;
	int mid=(l+r)>>1;
	return mx[k<<1|1]>val?min(ans[k],query(k<<1|1,mid+1,r,val)):query(k<<1,l,mid,val);
}

inline void pushup(cs int &k,cs int &l,cs int &r){
	mx[k]=max(mx[k<<1],mx[k<<1|1]);
	ans[k]=query(k<<1,l,(l+r)>>1,mx[k<<1|1]);
}

inline void build(int k,int l,int r){
	if(l==r){
		mx[k]=getint()-l;
		return ;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	pushup(k,l,r);
}

inline void modify(int k,int l,int r,int pos,int val){
	if(l==r){
		mx[k]=val-l;
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)modify(k<<1,l,mid,pos,val);
	else modify(k<<1|1,mid+1,r,pos,val);
	pushup(k,l,r);
}

signed main(){
	n=getint(),m=getint(),p=getint();
	build(1,1,n);
	cout<<(lasans=query(1,1,n,mx[1]-n)+n)<<"\n";
	while(m--){
		int x=getint()^(p*lasans),y=getint()^(p*lasans);
		modify(1,1,n,x,y);
		cout<<(lasans=query(1,1,n,mx[1]-n)+n)<<"\n"; 
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值