HNOI2018 转盘+线段树维护单调栈学习笔记

题目链接

题目简述

给定一个环形数组.你在时间为\(0\)时在一个你自己选的位置,每个时刻可以往下走或不走.如果你走到\(i\)的时刻\(\leq T_i\),那么可以把\(i\)标记.求把所有环上的点标记的最小时间.


解析

其实没有什么难的知识点.
但是好神仙啊\(QAQ\)

我们转化一下.假设时间\(t\)时在一个点,每次可以往前走或者不动.每个点会在\(T_i\)消失.求最小的\(t\).

转化后怎么做呢?
假设答案是\(t\),然后破环成链.
这都没什么好说的.
然后枚举一个点\(i\in (n,2*n]\),那么走到点\(j\)的时间是\(t-(i-j)\)
对于\(\forall j\in (i-n,i],t-(i-j)\geq T_j\)
简单移个项可得\(t\geq T_j-j+i\)
\(a_i=T_i-i\)
\(t\geq max\{a_j\}+i\)
\(t=min_{i=n}^{2*n-1}\{max_{j=i-n+1}^i\{a_j\}+i\}\)
再令\(i\leftarrow i+n-1\)
于是\(t=min_{i=1}^{n}\{max_{j=i}^{i+n-1}\{a_j\}+i\}+n-1\)
考虑到\(a_{k+n}<a_k\)
我们扩大一下范围,得到
\(t=min_{i=1}^{n}\{max_{j=i}^{2*n}\{a_j\}+i\}+n-1\)
对于一个\(i\),可以算它的最大后缀,但是好像并没有什么作用.

考虑\(j\)的答案.
我们分类讨论一下.

  • 如果\(a_j\)是最大后缀值,那么找到第一个\(i\),使得\(a_i>a_j\),贡献就是\(a_j+i+1\)
  • 如果\(a_j\)不是最大后缀值,那么找到它之后的第一个最大后缀值\(k\),答案就是\(k\)的答案.也就是说,\(a_j\)对答案没有贡献.

这启发了我们要使用单调栈来维护这个答案.
单调栈是从后往前单调增的.
由于还要支持修改,因此用一棵线段树即可.
其实这道题已经做完啦,接下来只是讲一下线段树维护单调栈的方法


线段树如何维护单调栈呢?
我们对于每个节点\([L,R]\)(编号是\(x\)),都维护一个\(Max\)表示区间最大值,再维护一个\(val\)表示\(min_{i=L}^{mid}\{max_{j=i}^{R}\{a_j\}+i\}\)
可以发现这个\(val\)就是用单调栈的方式维护一个答案.
我们令\(query(v,x)\)表示在\(x\)节点加入一个权值为\(v\)的点之后这个节点的\(val\)(即答案).
我们令\(lc\)表示\(x\)的左儿子,\(rc\)表示\(x\)的右儿子.

  • 如果\(v\leq Max_{rc}\),那么右儿子的答案与左儿子取\(min\)就是最终的答案.由于此时\(val_{x}\)就是维护左儿子的答案,因此\(val_x=min(val_x,query(v,rc))\).
  • 如果\(v>Max_{rc}\),相当于加入\(v\)之后所有的\([mid+1,R]\)中原来在单调栈中的元素都被弹出了.因此直接返回\(query(v,lc)\)即可.

我们计算一下时间复杂度(下面认为\(n,m\)同阶).
每次\(query\)的时间复杂度为\(T(n)=T(\frac{n}{2})+1=\log n\)
\(build\)每次是\(O(n*\log n)\)的,每个节点都要调用一次\(query\),因此是\(O(n*\log^2 n)\)的.每次\(modify\)\(log\)的,然后再调用一次\(query\),也是\(log^2\)的.因此总时间复杂度就是\(O(n*\log^2 n)\)的.

那么就完结撒花了!
代码如下
真的超级短

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#define N (200010)
#define inf (0x7f7f7f7f)
#define rg register int
#define Label puts("NAIVE")
#define spa print(' ')
#define ent print('\n')
#define rand() (((rand())<<(15))^(rand()))
typedef long double ld;
typedef long long LL;
typedef unsigned long long ull;
using namespace std;
inline char read(){
	static const int IN_LEN=1000000;
	static char buf[IN_LEN],*s,*t;
	return (s==t?t=(s=buf)+fread(buf,1,IN_LEN,stdin),(s==t?-1:*s++):*s++);
}
template<class T>
inline void read(T &x){
	static bool iosig;
	static char c;
	for(iosig=false,c=read();!isdigit(c);c=read()){
		if(c=='-')iosig=true;
		if(c==-1)return;
	}
	for(x=0;isdigit(c);c=read())x=((x+(x<<2))<<1)+(c^'0');
	if(iosig)x=-x;
}
inline char readchar(){
	static char c;
	for(c=read();!isalpha(c);c=read())
	if(c==-1)return 0;
	return c;
}
const int OUT_LEN = 10000000;
char obuf[OUT_LEN],*ooh=obuf;
inline void print(char c) {
	if(ooh==obuf+OUT_LEN)fwrite(obuf,1,OUT_LEN,stdout),ooh=obuf;
	*ooh++=c;
}
template<class T>
inline void print(T x){
	static int buf[30],cnt;
	if(x==0)print('0');
	else{
		if(x<0)print('-'),x=-x;
		for(cnt=0;x;x/=10)buf[++cnt]=x%10+48;
		while(cnt)print((char)buf[cnt--]);
	}
}
inline void flush(){fwrite(obuf,1,ooh-obuf,stdout);}
struct xds{
	int l,r,mx,val;
}a[N<<3];
int n,m,w[N],pp,lans;
int query(int v,int x){
	if(a[x].l==a[x].r){
		if(a[x].mx>v)return v+a[x].l;
		else return inf;
	}
	if(a[x*2+1].mx>v)return min(query(v,x*2+1),a[x].val);
	else return query(v,x*2);
}
void pushup(int x){
	a[x].mx=max(a[x*2].mx,a[x*2+1].mx);
	a[x].val=query(a[x*2+1].mx,x*2);
}
void build(int l,int r,int x){
	a[x].l=l,a[x].r=r;
	if(l==r){
		a[x].mx=w[l]-l;
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,x*2),build(mid+1,r,x*2+1);
	pushup(x);
}
void modify(int k,int v,int x){
	if(a[x].l==a[x].r){
		a[x].mx=v-a[x].l;
		return;
	}
	int mid=(a[x].l+a[x].r)>>1;
	if(k<=mid)modify(k,v,x*2);
	else modify(k,v,x*2+1);
	pushup(x);
}
int main(){
	read(n),read(m),read(pp);
	for(int i=1;i<=n;i++)read(w[i]);
	build(1,n,1);
	print(lans=query(a[1].mx-n,1)+n),ent;
	while(m--){
		int x,y;
		read(x),read(y);
		if(pp)x^=lans,y^=lans;
		modify(x,y,1);
		print(lans=query(a[1].mx-n,1)+n),ent;
	}
	return flush(),0;
}
posted @ 2018-12-05 14:42  Romeolong  阅读(394)  评论(0编辑  收藏  举报