「AHOI / HNOI2018」转盘 解题报告

「AHOI / HNOI2018」转盘

可能是我语文水平不太行...


首先可以猜到一些事实,这个策略一定可以被一个式子表示出来,不然带修修改个锤子。

然后我们发现,可以枚举起点,然后直接往前走,如果要等就等到它出现。

因为如果不等,一定要走超过一圈,这样一定不如从它后面那个点当起点。

既然要等,不如我们就在起点等了,显然这样的等价的,于是我们可以搞出这个式子了。

\[\min_{i=1}^n(\max_{j=i}^{i+n-1}S_j-j+i)+n-1 \]

这里我们把\(S\)倍长了

稍微放缩一下并扔掉不重要的东西,我们要维护的大概是这个东西

\[\min_{i=1}^ni+(\max_{j=i}^{2n}T_j) \]

我们每次修改\(T_j\),然后询问这个式子的值

考虑对\(j\)枚举\(i\),有一些事实是

如果\(T_{p_1},T_{p_2},T_{p_3},\dots,T_{p_k}\)\(T\)的后缀最大值集合

我们的答案就是

\[\min_{i=1}^kT_i+p_{i-1}+1 \]

后缀最大值可以以单调栈的形式表示出来

考虑在线段树上维护

比如说,我们可以

我们每个节点维护一个右儿子最大值与左儿子整个后缀最大值集合连接成的答案集合。

有点抽象,举个例子就是

比如有这样的\(T\)

左儿子:5 4 3 1 右儿子:2 1 0

那么这个信息就是5 4 3 2这个集合的答案

也不一定非要这样,但是这样蛮方便的

维护这个信息得去左儿子上二分,所以这样是\(\log^2 n\)

这个题,你发现,\(2n\)的区间中,右儿子内部不能产生贡献(因为式子中\(\min\)的枚举范围是\(1\sim n\)的)

所以维护一下长为\(n\)的区间的答案,每次询问的时候在\((n+1)\sim 2n\)里面找个最大值去问就可以了,注意到这个东西它可以从\(1\sim n\)的最大值里面获得


Code:

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
using std::min;
using std::max;
const int SIZE=1<<21;
char ibuf[SIZE],*iS,*iT;
//#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
#define gc() getchar()
template <class T>
void read(T &x)
{
	int f=0;x=0;char c=gc();
	while(!isdigit(c)) f|=c=='-',c=gc();
	while(isdigit(c)) x=x*10+c-'0',c=gc();
	if(f) x=-x;
}
const int N=2e5+10;
const int inf=0x3f3f3f3f;
int n,m,p,t[N];
int mx[N<<2],yuu[N<<2];
#define ls id<<1
#define rs id<<1|1
int qry(int id,int l,int r,int d)
{
	if(l==r) return mx[id]>d?d+l:inf;
	int mid=l+r>>1;
	if(mx[rs]>d) return min(yuu[id],qry(rs,mid+1,r,d));
	else return qry(ls,l,mid,d);
}
void updata(int id,int l,int r)
{
	mx[id]=max(mx[ls],mx[rs]);
	yuu[id]=qry(ls,l,l+r>>1,mx[rs]);
}
void upd(int id,int l,int r,int p,int d)
{
	if(l==r)
	{
		mx[id]=d;
		return;
	}
	int mid=l+r>>1;
	if(p<=mid) upd(ls,l,mid,p,d);
	else upd(rs,mid+1,r,p,d);
	updata(id,l,r);
}
void build(int id,int l,int r)
{
	if(l==r)
	{
		mx[id]=t[l];
		yuu[id]=inf;
		return;
	}
	int mid=l+r>>1;
	build(ls,l,mid),build(rs,mid+1,r);
	updata(id,l,r);
}
int main()
{
	read(n),read(m),read(p);
	for(int i=1;i<=n;i++) read(t[i]),t[i]-=i;
	build(1,1,n);
	int ans=qry(1,1,n,mx[1]-n)+n;
	printf("%d\n",ans);
	for(int x,y,i=1;i<=m;i++)
	{
		read(x),read(y);
		if(p) x^=ans,y^=ans;
		upd(1,1,n,x,y-x);
		printf("%d\n",ans=qry(1,1,n,mx[1]-n)+n);
	}
	return 0;
}

2019.5.21

posted @ 2019-05-21 21:26  露迭月  阅读(236)  评论(0编辑  收藏  举报