P4425 [HNOI/AHOI2018]转盘 线段树维护单调栈

题意:

戳这里

分析:

  • 暴力

    只会 (40pts) 的暴力还让我写成了 (30pts) ,首先我们会在链上处理这个问题,(f_i=max(t_i,f_{i-1}+1))

    转化一下式子

    (f_i-i=max(t_i-i,f_{i-1}-(i-1)))

    我们记 (A_i=f_i-i)

    ( herefore A_i=max(t_i-i,A_{i-1}))

    ( f_i=A_i+i=max(t_j-j)+i)

    所以采取倍增然后断环为链的方式,维护一下每一个长度为 (n) 的区间里面 (t_j-j) 的最大值,用单调队列可以做到总复杂度 (O(mn)) 的然后我一看见单点修改,区间查询 想都没想直接上线段树,凭空多出来一个 (log)


  • 正解:

    我们接着上面的式子

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

    更改一下 (j) 的枚举得到

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

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

    然后关键的一步来了

    (ecause t_i-i>t_{i+n}-(i+n)) ( herefore [i,n+i-1]) 的最大值 等价于 ([i,2n]) 的最大值,也就是说我们不需要维护区间最值了,直接维护全局的后缀最值就可以了

    虽然这种转换目前来看似乎不能直接优化复杂度

    但是,我们接着考虑,每一个固定的 (t_j-j) 对应一段固定后缀,那么对于 (j) ,它取得最优值(即 (max(t_j-j+i) ) 的地方是 (i) 满足 (t_{i-1}-(i-1)>t_j-j) 也就是在它前面第一个比它大的元素的后一位 ,这也就告诉我们只需要维护一个类似单调栈的结构,就能直接求出每一段最优值,但是我们需要这个单调栈支持动态修改,做过 楼房重建 的巨佬就会发现,这道题就是线段树维护单调栈

    放到这个题上具体来说就是:

    建一颗线段树,每一个节点维护一个 (mx,val) 其中 (mx) 表示区间 (t_i-i) 的最大值, (val) 表示区间 ((t_j-j)+i) 的最小值 ( 这个 (val) 就相当于单调栈中一个元素,和它前面第一个比他大的元素的下一位的下标之和)

    每一次 (pushup) 的时候,对于 (mx) 直接区间取 (max) ,而对于 (val) 要进行二分查找,这个区间的 (val) 就是用右区间的 (mx) 在左区间中找到一个位置 (i) 满足 (i)(t_i-i) 小于 (mx) 的下标最小的值,查找的时候顺便和子区间的 (val) 取一下 (min)

    总复杂度 (O(mlog ^2)) 线段树上二分两只 ( log)

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second
#define inl inline
#define reg register

using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 3e5+5;
	int n,m,lst,p;
	int mx[maxn<<2],val[maxn<<2],t[maxn<<1];
	
	int query(int rt,int l,int r,int x)
	{
		if(l==r) return l+max(x,mx[rt]);
		int mid=(l+r)>>1;
		if(mx[rc]<=x) return min(mid+1+x,query(lc,l,mid,x));
		else return min(val[rt],query(rc,mid+1,r,x));
	}
	
	void pushup(int rt,int l,int r)
	{
		int mid=(l+r)>>1;
		mx[rt]=max(mx[lc],mx[rc]);
		val[rt]=query(lc,l,mid,mx[rc]);		
	}
	
	void modify(int rt,int l,int r,int pos,int x)
	{
		if(l==r)
		{
			mx[rt]=x-l;
			val[rt]=x;
			return ;
		}
		int mid=(l+r)>>1;
		if(pos<=mid) modify(lc,l,mid,pos,x);
		else modify(rc,mid+1,r,pos,x);
		pushup(rt,l,r);
	}
	
	void build(int rt,int l,int r)
	{
		if(l==r)
		{
			mx[rt]=t[l]-l;
			val[rt]=t[l];
			return ;
		}
		int mid=(l+r)>>1;
		build(lc,l,mid);build(rc,mid+1,r);
		pushup(rt,l,r);
	}
	
	void work()
	{
		int x,y;
		n=read();m=read();p=read();
		for(int i=1;i<=n;i++) t[i]=t[i+n]=read();
		build(1,1,2*n);
		printf("%d
",lst=val[1]+n-1);
		while(m--)
		{
			x=read()^(lst*p);y=read()^(lst*p);
			modify(1,1,2*n,x,y);
			modify(1,1,2*n,x+n,y);
			printf("%d
",lst=val[1]+n-1);
		}
	}

}

int main()
{
	zzc::work();
	return 0;
}

原文地址:https://www.cnblogs.com/youth518/p/14282179.html