2021牛客暑期多校训练营7 部分题题解

Posted Alkaid~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021牛客暑期多校训练营7 部分题题解相关的知识,希望对你有一定的参考价值。

B.xay loves monotonicity

题目链接

xay loves monotonicity

简要题解

不难看出这是一道维护序列信息的数据结构题。
我们需要支持单点修改\\(A\\)序列,区间修改\\(B\\)序列,以及区间查询一个特殊的值。
具体地说,对于一个询问,我们要取出序列\\(A\\)在这个区间内,包含左端点的最长不降子序列,并把子序列的位置对应到序列\\(B\\)中得到一个\\(0/1\\)串。
我们需要求这个\\(0/1\\)串中有多少个值不同的相邻对。

这可以说是一个经典题,应该也可以说是一个套路题。
如果我们要在一个区间内求一个最长不降子序列,那么这个区间内的最大值一定在子序列中,并且这个最大值一定在序列末尾。
基于这个性质,两个区间的信息就可以合并,而线段树很适合维护多个区间合并的信息。

我们在线段树上维护区间内\\(A\\)序列的最大值,以及这个最大值所在位置对应的\\(B\\),若有多个最大值则维护最右边的那一个。
我们需要解决的问题可以用一个\\(Calc(l,r,a,b)\\)来描述:如果当前序列末尾的数为\\(a\\),其对应的\\(B\\)值为\\(b\\),那么\\([l,r]\\)这段区间可以提供多少贡献。
很明显,加上这段区间后,序列的末尾,要么是这段区间的最大值,要么是原来的\\(a\\),于是我们可以接着往后面合并区间。
线段树可以将一个询问区间分成\\(logn\\)个线段树节点维护的区间,我们已经知道如何合并区间,现在只需要对每个区间快速计算\\(Calc(l,r,a,b)\\)
对于线段树节点维护的一个区间来说,如果左边\\(A\\)序列的最大值小于\\(a\\),那么左边就没有贡献,直接递归右边。
否则,合并左半边区间后,不降子序列的末尾就是左半边序列的最大值,设为\\(a_l\\)
而我们已经知道\\(a_l\\)以及对应的\\(b_l\\),只要提前算出右半边区间的\\(Calc(l,r,a_l,b_l)\\),记为\\(C\\),就可以直接加上贡献,只递归左边。
我们从下往上维护每个区间的\\(C\\),每次维护需要\\(logn\\)的递归计算,因此总复杂度是\\(O(nlog^2n)\\)的。

因此,我们需要在线段树上维护三个值:\\(A\\)序列的区间最大值\\(a\\)\\(a\\)\\(B\\)序列对应位置的值\\(b\\),当左区间最大值作为子序列末尾时右区间的\\(C\\)值。
对于\\(A\\)序列和\\(B\\)序列的修改,就是单点修改与区间修改,修改完及时\\(Push\\_up\\)维护\\(C\\)值即可。
对于一个区间的询问,在线段树上可以拆成\\(logn\\)个区间,依次对每个区间调用\\(Calc\\)函数,再合并即可。
代码如下:

#include<bits/stdc++.h>
using namespace std;
const int MAXN=2e5+10;
int n,Qs,A[MAXN],B[MAXN];
int Read()
{   int a=0,c=1;   char b=getchar();
    while(b!=\'-\'&&(b<\'0\'||b>\'9\')) b=getchar();
    if(b==\'-\') c=-1,b=getchar();
    while(b>=\'0\'&&b<=\'9\') a=a*10+b-48,b=getchar();
    return a*c;
}
int Max(int A,int B){   return A>B?A:B;   }
namespace TREE
{   struct ELE{   int Mv,Ma,Mb;   };
    int Maxa[MAXN*4],Maxl[MAXN*4];
    bool Maxb[MAXN*4],Lan[MAXN*4];
    ELE operator + (ELE A,ELE B)
    {   if(B.Ma>=A.Ma) return (ELE){A.Mv+B.Mv,B.Ma,B.Mb};
        return (ELE){A.Mv+B.Mv,A.Ma,A.Mb};
    }
    void Push_down(int S)
    {   if(!Lan[S]) return;
        Maxb[S<<1]^=1,Lan[S<<1]^=1,Maxb[S<<1|1]^=1,Lan[S<<1|1]^=1,Lan[S]=0;
    }
    int Calc(int S,int Le,int Ri,int Num,int Nb,int Mid=0)
    {   if(Le==Ri) return Maxa[S]>=Num?Maxb[S]!=Nb:0;
        Mid=(Le+Ri)>>1,Push_down(S);
        return Maxa[S<<1]>=Num?Calc(S<<1,Le,Mid,Num,Nb)+Maxl[S]:Calc(S<<1|1,Mid+1,Ri,Num,Nb);
    }
    void Push_up(int S,int Le,int Ri,int Mid=0)
    {   if(Maxa[S<<1|1]<Maxa[S<<1]) Maxa[S]=Maxa[S<<1],Maxb[S]=Maxb[S<<1];
        else Maxa[S]=Maxa[S<<1|1],Maxb[S]=Maxb[S<<1|1];
        Mid=(Le+Ri)/2,Maxl[S]=Calc(S<<1|1,Mid+1,Ri,Maxa[S<<1],Maxb[S<<1]);
    }
    void Build(int S,int Le,int Ri,int Mid=0)
    {   if(Le==Ri) return Maxa[S]=A[Le],Maxb[S]=B[Le],(void)0;
        Mid=(Le+Ri)>>1,Build(S<<1,Le,Mid),Build(S<<1|1,Mid+1,Ri),Push_up(S,Le,Ri);
    }
    void Modify1(int S,int Le,int Ri,int Aim,int Num,int Mid=0)
    {   if(Le==Ri) return Maxa[S]=Num,(void)0;
        Mid=(Le+Ri)>>1,Push_down(S);
        Aim<=Mid?Modify1(S<<1,Le,Mid,Aim,Num):Modify1(S<<1|1,Mid+1,Ri,Aim,Num),Push_up(S,Le,Ri);
    }
    void Modify2(int S,int Le,int Ri,int Al,int Ar,int Mid=0)
    {   if(Al<=Le&&Ri<=Ar) return Lan[S]^=1,Maxb[S]^=1,(void)0;
        Mid=(Le+Ri)/2,Push_down(S);
        if(Al<=Mid) Modify2(S<<1,Le,Mid,Al,Ar),Push_up(S,Le,Ri);
        if(Mid<Ar) Modify2(S<<1|1,Mid+1,Ri,Al,Ar),Push_up(S,Le,Ri);
        Push_up(S,Le,Ri);
    }
    ELE Query(int S,int Le,int Ri,int Al,int Ar,int Num,int Nb,int Mid=0)
    {   if(Al<=Le&&Ri<=Ar)
        {   if(Maxa[S]<Num) return (ELE){0,Num,Nb};
            return (ELE){Calc(S,Le,Ri,Num,Nb),Maxa[S],Maxb[S]};
        }
        Mid=(Le+Ri)>>1,Push_down(S);
        ELE Ra=(ELE){0,Num,Nb},Rb=(ELE){0,Num,Nb};
        if(Al<=Mid) Ra=Query(S<<1,Le,Mid,Al,Ar,Num,Nb);
        if(Mid<Ar) Rb=Query(S<<1|1,Mid+1,Ri,Al,Ar,Ra.Ma,Ra.Mb);
        return Ra+Rb;
    }
    int Query2(int S,int Le,int Ri,int Aim,int Mid=0)
    {   if(Le==Ri) return Maxb[S];
        return Push_down(S),Mid=(Le+Ri)/2,Aim<=Mid?Query2(S<<1,Le,Mid,Aim):Query2(S<<1|1,Mid+1,Ri,Aim);
    }
}using namespace TREE;
int main()
{   n=Read();
    for(int i=1;i<=n;i++) A[i]=Read();
    for(int i=1;i<=n;i++) B[i]=Read();
    Build(1,1,n),Qs=Read();
    for(int i=1,K,T1,T2;i<=Qs;i++)
    {   K=Read(),T1=Read(),T2=Read();
        if(K==1) Modify1(1,1,n,T1,T2),A[T1]=T2;
        if(K==2) Modify2(1,1,n,T1,T2);
        if(K==3) printf("%d\\n",Query(1,1,n,T1,T2,A[T1],Query2(1,1,n,T1)).Mv);
    }
}

以上是关于2021牛客暑期多校训练营7 部分题题解的主要内容,如果未能解决你的问题,请参考以下文章

"蔚来杯"2022牛客暑期多校训练营6部分题题解

2021牛客暑期多校训练营7,签到题FHI

2021牛客暑期多校训练营1 - F - Find 3-friendly Integers - 题解

2021牛客暑期多校训练营1 - F - Find 3-friendly Integers - 题解

2021牛客暑期多校训练营1 - D - Determine the Photo Position - 题解

2021牛客暑期多校训练营1 - A - Alice and Bob - 题解