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

传送门:bzoj5286


题解

考虑是个环,把 T T T数组双倍复制一下,得到:
a n s = m i n i = 1 n ( m a x j = i i + n − 1 ( T j − ( j − i ) ) ) + n − 1 ans=min_{i=1}^n (max_{j=i} ^{i+n-1}(T_j-(j-i)))+n-1 ans=mini=1n(maxj=ii+n1(Tj(ji)))+n1

证明:

m a x i = 1 n T i &lt; 2 × ( n − 1 ) max_{i=1}^n T_i&lt;2\times (n-1) maxi=1nTi<2×(n1)时,显找到最小的差 m i n i = 1 n ( m a x j = i i + n − 1 ( T j − ( j − i ) ) ) min_{i=1} ^n(max_{j=i}^{i+n-1}(T_j-(j-i))) mini=1n(maxj=ii+n1(Tj(ji)))后在起点等待这么久后,就可以畅通无阻地走完一圈,而答案是最优的。

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

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

再观察枚举起点后的取答案: m a x j = i i + n − 1 ( T j − ( j − i ) ) max_{j=i}^{i+n-1}(T_j-(j-i)) maxj=ii+n1(Tj(ji))

实际上等价于 m a x j = i 2 n ( T j − j ) + i max_{j=i}^{2n}(T_j-j)+i maxj=i2n(Tjj)+i,因为显然 T i − i &gt; T i + n − ( i + n ) ( T i = T i + n , 1 ≤ i ≤ n ) T_i-i&gt;T_{i+n}-(i+n)(T_i=T_{i+n},1\leq i\leq n) Tii>Ti+n(i+n)(Ti=Ti+n,1in),这样化简后等价于求一个后缀最大,处理出 x i = m a x j = i 2 n ( T j − j ) x_i=max_{j=i}^{2n} (T_j-j) xi=maxj=i2n(Tjj),则
a n s = m i n i = 1 n ( x i + i ) + n − 1 ans=min_{i=1}^n (x_i+i)+n-1 ans=mini=1n(xi+i)+n1

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

考虑线段树如何优化单调队列:
线段树节点 k k k(管辖范围 [ l , r ] [l,r] [l,r])上存两个信息: m x k , v a l k mx_k,val_k mxk,valk 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 ) ) mx_k=max_{i=l}^r (T_i-i),val_k=min_{i=l}^{mid}(x_i+i)\ (x_i=max_{j=i}^r (T_j-j)) mxk=maxi=lr(Tii),valk=mini=lmid(xi+i) (xi=maxj=ir(Tjj))
(线段树根节点范围为 [ 1 , 2 n ] [1,2n] [1,2n],查询直接回答 v a l r o o t + n − 1 val_{root}+n-1 valroot+n1)。

考虑如何 p u s h u p pushup pushup

设当前节点为 k k k(管辖区间为 [ l , r ′ ] [l,r&#x27;] [l,r]),左儿子为 l s ls ls(管辖区间为 [ l , r ] [l,r] [l,r]),右儿子为 r s rs rs(管辖区间为 [ r + 1 , r ′ ] [r+1,r&#x27;] [r+1,r])。

m x k = m a x ( m x l s , m x r s ) mx_k=max(mx_{ls},mx_{rs}) mxk=max(mxls,mxrs)

对于 v a l k = m i n i = l r ( x i + i ) val_k=min_{i=l}^r (x_i+i) valk=mini=lr(xi+i),由 l s ls ls可得 m i n i = l m i d ( x i + i ) min_{i=l} ^ {mid} (x_i+i) mini=lmid(xi+i),而 m i n i = m i d + 1 r ( x i + i ) min_{i=mid+1}^{r} (x_i+i) mini=mid+1r(xi+i),通过 q u e r y ( n w , m x x , l , r ) query(nw,mxx,l,r) query(nw,mxx,l,r)递归得到(底层为 m a x ( m x n w , m x x ) max(mx_{nw},mxx) max(mxnw,mxx)),分类讨论一下 q u e r y ( l s , m x r s , l , r ) query(ls,mx_{rs},l,r) query(ls,mxrs,l,r)

  • m x l s r s ≥ m x r s mx_{ls_{rs}}\geq mx_{rs} mxlsrsmxrs时, v a l l s l s val_{ls_{ls}} vallsls不变,只需要修改 l s r s ls_{rs} lsrs,返回 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 ) ) min(val_{ls},query(ls_{rs},mx_{rs},mid+1,r)) min(valls,query(lsrs,mxrs,mid+1,r))

  • m x l s r s &lt; m x r s mx_{ls_{rs}}&lt;mx_{rs} mxlsrs<mxrs时, v a l l s val_{ls} valls整个都会统一增大,但不确定 m x l s l s mx_{ls_{ls}} mxlsls m x r s mx_{rs} mxrs的关系,返回 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 ) ) min(mid+1+mx_{rs},query(ls_{ls},mx_{rs},l,mid)) min(mid+1+mxrs,query(lsls,mxrs,l,mid))

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


代码

#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("");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值