【BZOJ】5286: [Hnoi2018]转盘 -线段树优化单调队列

版权声明:转载请附带链接或评论 https://blog.csdn.net/corsica6/article/details/82181649

传送门:bzoj5286


题解

考虑是个环,把 T 数组双倍复制一下,得到:
a n s = m i n i = 1 n ( m a x j = i i + n 1 ( T j ( j i ) ) ) + n 1

证明:

m a x i = 1 n T i < 2 × ( n 1 ) 时,显找到最小的差 m i n i = 1 n ( m a x j = i i + n 1 ( T j ( j i ) ) ) 后在起点等待这么久后,就可以畅通无阻地走完一圈,而答案是最优的。

m a x i = 1 n T i 2 × ( n 1 ) 时,从某一起点出发,有可能先走完一圈其他点后再绕回这个最大点,答案最优,而如果按照上面的式子算,显然不是最优的(这也是问题所在,如下图(出发点为蓝点,红点 T i 2 × ( n 1 ) ))。
这里写图片描述
考虑在走完一圈后,红蓝之间的点显然是多走了一次的,而这些时间应该包含在等待 T m a x 中,而不是额外再算,但完全可以从绿点出发,对于绿点来说,先走和后走就没什么区别了,上式也就满足了最优。

故对于所有情况,都存在上式可以取到最优解。

再观察枚举起点后的取答案: m a x j = i i + n 1 ( T j ( j i ) )

实际上等价于 m a x j = i 2 n ( T j j ) + i ,因为显然 T i i > T i + n ( i + n ) ( T i = T i + n , 1 i n ) ,这样化简后等价于求一个后缀最大,处理出 x i = m a x j = i 2 n ( T j j ) ,则
a n s = m i n i = 1 n ( x i + i ) + n 1

没有修改的询问直接 O ( n ) 查询即可。
有修改的询问用线段树优化。

考虑线段树如何优化单调队列:
线段树节点 k (管辖范围 [ l , r ] )上存两个信息: m x k , v a l k m x k = m a x i = l r ( T i i ) , v a l k = m i n i = l m i d ( x i + i )   ( x i = m a x j = i r ( T j j ) )
(线段树根节点范围为 [ 1 , 2 n ] ,查询直接回答 v a l r o o t + n 1 )。

考虑如何 p u s h u p

扫描二维码关注公众号,回复: 3185846 查看本文章

设当前节点为 k (管辖区间为 [ l , r ] ),左儿子为 l s (管辖区间为 [ l , r ] ),右儿子为 r s (管辖区间为 [ r + 1 , r ] )。

m x k = m a x ( m x l s , m x r s )

对于 v a l k = m i n i = l r ( x i + i ) ,由 l s 可得 m i n i = l m i d ( x i + i ) ,而 m i n i = m i d + 1 r ( x i + i ) + r l ,通过 q u e r y ( n w , m x x , l , r ) 递归得到(底层为 m a x ( m x n w , m x x ) ),分类讨论一下 q u e r y ( l s , m x r s , l , r )

  • m x l s r s m x r s 时, v a l l s l s 不变,只需要修改 l s r s ,返回 m i n ( v a l l s , q u e r y ( l s r s , m x r s , m i d + 1 , r ) )

  • m x l s r s < m x r s 时, v a l l s 整个都会统一增大,但不确定 m x l s l s m x r s 的关系,返回 m i n ( m i d + 1 + m x r s , q u e r y ( l s l s , m x r s , l , m i d ) )

复杂度 O ( ( n + m ) l o g 2 n )


代码

#include<bits/stdc++.h>
#define gc getchar
#define si isdigit
#define mid (((l)+(r))>>1)
#define lc k<<1
#define rc k<<1|1
using namespace std;
const int N=2e5+100;
int n,m,a[N],op,ans;
int mx[N<<2],val[N<<2];

char c;
inline int rd()
{
    c=gc();int x=0;
    for(;!si(c);c=gc());
    for(;si(c);c=gc()) x=x*10+(c^48);
    return x;
}

inline int get(int ql,int mxx,int l,int r)
{
    if(l==r) return max(mx[ql],mxx)+l;
    if(mx[ql<<1|1]>=mxx) return min(val[ql],get(ql<<1|1,mxx,mid+1,r));
    return min(mid+1+mxx,get(ql<<1,mxx,l,mid));
}

inline void update(int k,int l,int r)
{
    mx[k]=max(mx[lc],mx[rc]);
    val[k]=get(lc,mx[rc],l,mid);
}

inline void build(int k,int l,int r)
{
    if(l==r){val[k]=a[l]+l;mx[k]=a[l];return;}
    build(lc,l,mid);build(rc,mid+1,r);
    update(k,l,r);
}

inline void change(int k,int l,int r,int pos)
{
    if(l==r) {val[k]=a[l]+l;mx[k]=a[l];return;}
    if(pos<=mid) change(lc,l,mid,pos);
    else change(rc,mid+1,r,pos);
    update(k,l,r);
}

inline void out(int x){if(x>9) out(x/10);putchar('0'+x%10);}

int main(){
    int i,j,x,y;
    n=rd();m=rd();op=rd();
    for(i=1;i<=n;++i){a[i]=rd()-i;a[i+n]=a[i]-n;}
    build(1,1,n<<1);
    out((ans=val[1]+n-1));puts("");
    for(;m;--m){
        x=rd();y=rd();
        if(op) x^=ans,y^=ans;
        a[x]=y-x;a[x+n]=y-x-n;
        change(1,1,n<<1,x);change(1,1,n<<1,x+n);
        out((ans=val[1]+n-1));puts("");
    }
}

猜你喜欢

转载自blog.csdn.net/corsica6/article/details/82181649