zoukankan      html  css  js  c++  java
  • 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;
    }
    
    
  • 相关阅读:
    转: 尽己力,无愧于心 存储过程
    转: 尽己力,无愧于心 FastReport.Net 常用功能总汇
    关于Ansi_Nulls、Quoted_Identifier、Ansi_Padding的用法
    数学建模
    数学建模
    深度学习
    C#复习
    Keras的两种网络模型
    xaml转义
    C# 文件目录操作
  • 原文地址:https://www.cnblogs.com/youth518/p/14282179.html
Copyright © 2011-2022 走看看