[HNOI/AHOI2018]转盘【线段树】【单调栈】

传送门

一旦我走到的点已经出现,我就打标记。问最小时间。

我们发现,如果要靠走两圈或者更多圈来得到的点,和一开始在起点等很久再出发得到的答案是等价的。

比如说走了很多圈经过点数分别为1,8,19,23,555

完全可以变成551,552,553,554,555

所以问题转化成了只走一圈。

首先拆掉环复制一遍变成一条链。

我们需要枚举起点,得到答案的式子是这样的

Ans=min_{i=1}^{n}(max_{j=0}^{n-1}(t_{i+j}-j)+n-1)

即通过每一个点的时间来确定起点出发时间,需要取最大。

Ans=min_{i=1}^n({max_{j=i}^{n+i-1}(t_j+i-j}))+n-1

x_i=t_i-i

Ans=min_{i=1}^n(max_{j=i}^{n+i-1}(x_j)+i)+n-1

然而我们可以发现

[i\subset [1,n]]x_i=t_i-i,x_{i+n}=t_i-i-n;\therefore x_i>x_{i+n}

\therefore Ans=min_{i=1}^n(max_{j=i}^n(x_j)+i)+n-1

对于一个j来说,所有能取到x_j作为其后缀最大值的i中,i越小越好。这告诉我们可以维护一个单调递减的单调栈。

对于单调栈中的结点,每个结点对答案的贡献就是f_{i-1}+1+x_{f_i}

把这个1和答案最后那个-1抵消得到Ans=min(f_i+x_{f_{i+1}})+n

对于中间那个维护,单调栈即可。对于这个单调栈,右边都可以用,然后拿右边最大值到左边里面去二分取最小。

不过,我们每个节点的key实际上保存的是其左儿子的key。这样方便我们查询。

还有个技巧。

我们拆了环,按理来说应该维护长度为2n的序列。不过我们发现,右边的复制序列在数值更新上和左边完全相同,唯一的区别就是右边的x比左边对应的x要少n。那我们完全可以最后合并一次,用全局最大(也就是右边最大)减去n,然后在左边二分。

#include<bits/stdc++.h>
using namespace std;
#define in read()
int in{
	int cnt=0,f=1;char ch=0;
	while(!isdigit(ch)){
		ch=getchar();if(ch=='-')f=-1;
	}
	while(isdigit(ch)){
		cnt=cnt*10+ch-48;
		ch=getchar();
	}return cnt*f;
}
struct node{
	int l,r,maxx,key;
}t[1600003];
int n,m,type;
int last;
int a[200003];
int query(int u,int key){
	if(t[u].l==t[u].r)return t[u].maxx>key?key+t[u].l:0x3f3f3f3f;
	if(t[u*2+1].maxx>key)return min(t[u].key,query(u*2+1,key));
	else return query(u*2,key);
}
void pushup(int u){
	t[u].maxx=max(t[u*2].maxx,t[u*2+1].maxx);
	//int mid=(t[u].l+t[u].r)>>1;
	//t[u].key=min(t[u*2+1].key,query(u*2,t[u*2+1].maxx));
	t[u].key=query(u*2,t[u*2+1].maxx);
}
void modify(int u,int pos){
	if(t[u].l>pos||t[u].r<pos)return;
	if(t[u].l==t[u].r&&t[u].r==pos){
		t[u].maxx=a[pos]-pos;return;
	}int mid=(t[u].l+t[u].r)>>1;
	if(pos<=mid)modify(u*2,pos);else modify(u*2+1,pos);
	pushup(u);
}
void build(int u,int l,int r){
	t[u].l=l;t[u].r=r;if(l==r){
		t[u].maxx=a[l]-l;return;
	}int mid=(l+r)>>1;
	build(u*2,l,mid);build(u*2+1,mid+1,r);
	pushup(u);
}

signed main(){
	n=in;m=in;type=in;
	for(int i=1;i<=n;i++)a[i]=a[i+n]=in;
	build(1,1,n);last=query(1,t[1].maxx-n)+n;
	cout<<last<<'\n';
	while(m--){
		int x=in;int y=in;if(type)x^=last,y^=last;a[x]=y;
		modify(1,x);
		last=query(1,t[1].maxx-n)+n;
		cout<<last<<'\n';
	}
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值