题目链接
题目简述
给定一个环形数组.你在时间为(0)时在一个你自己选的位置,每个时刻可以往下走或不走.如果你走到(i)的时刻(leq T_i),那么可以把(i)标记.求把所有环上的点标记的最小时间.
解析
其实没有什么难的知识点.
但是好神仙啊(QAQ)
我们转化一下.假设时间(t)时在一个点,每次可以往前走或者不动.每个点会在(T_i)消失.求最小的(t).
转化后怎么做呢?
假设答案是(t),然后破环成链.
这都没什么好说的.
然后枚举一个点(iin (n,2*n]),那么走到点(j)的时间是(t-(i-j))
对于(forall jin (i-n,i],t-(i-j)geq T_j)
简单移个项可得(tgeq T_j-j+i)
令(a_i=T_i-i)
(tgeq max{a_j}+i)
(t=min_{i=n}^{2*n-1}{max_{j=i-n+1}^i{a_j}+i})
再令(ileftarrow 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)的右儿子.
- 如果(vleq 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('
')
#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;
}