1 条题解

  • 0
    @ 2025-8-24 22:07:17

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar TheLostWeak
    **

    搬运于2025-08-24 22:07:17,当前版本为作者最后更新于2018-12-24 20:30:02,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    在博客查看

    大致题意: 给你一个序列,要你支持三种操作:区间赋值,区间求和,撤回之前任一区间赋值操作。


    分块

    这道题应该是一道十分毒瘤分块题。

    这道题要用到的算法并不是很难,但是思维难度是真的高。

    大致思路就是对于每一个块维护一大堆信息,各用一个栈存储对每个块的赋值操作,并开一个邻接表来存储对每个元素的赋值操作,防止MLEMLE

    具体的操作方法见下。


    区间赋值

    对于不完整的块,我们用邻接表来更新这些元素的赋值操作,然后重构块(关于重构详见后文)。

    对于完整的块,我们用邻接表来更新这个块的赋值操作,然后更新这个块的ansans块的大小*赋的值


    区间求和

    对于不完整的块,我们暴力求解其答案。对于每一个元素要进行如下分类讨论:

    • 如果该块没有被整块赋值过。则对于每个元素,我们判断其是否被赋值过,如果赋值过,将resres加上最后一次赋的值,否则将resres加上该元素的初始值。
    • 如果该块被整块赋值过。则对于每个元素,我们选择该块最后一次被赋值该元素最后一次被赋值这两种情况中较后一次所赋的值,然后将resres加上这个值。

    对于完整的块,我们直接加上该块的ansansansans会在每次操作中更新,因此一定是当前的答案)。


    撤销区间赋值

    这应该是最恶心的一个操作了。

    我们可以用一个flagflag数组来标记每一个操作是否被撤销。

    考虑每次撤销操作时,如果该元素/块在这一次操作之后还有未被撤销的赋值操作,则此次撤销其实对答案暂时是没有任何影响的。

    因此,对于不完整的块,我们只要将每个元素邻接表中最后面的一些已被撤销的操作全部清空,然后重构块即可(关于重构详见后文)。

    而对于完整的块,我们首先依然是将该块的栈顶已被撤销的操作全部清空,然后要依据重构块时所保留下来的一些信息来对ansans进行修改。

    由于重构的部分还未介绍,因此这一部分也放后面讲。


    重构

    块的重构应该也是这道题比较恶心的地方之一。

    首先,我们要对块内每一个元素最后一次被赋值的时间进行一次基数排序

    这样一来,这些元素就按照最后一次被赋值的时间从小到大被排好序了。

    然后,我们倒着对被赋的值求一次后缀和,用一个数组resres来存储。

    千万要注意的是,resires_i存储的是从i+1i+1个元素开始的后缀和

    还有,一定要记得将res0res_0加上所有未被修改过的元素的初始值

    接下来,我们要使用两个变量lstlstpp,它们在后面的块内撤销操作中也会发挥作用。

    我们用lstlst来记录当前堆顶的元素值,它的实际意义即为最后一次整块修改的时间

    然后,我们用pp00开始枚举,找到最后一个单个元素赋值时间小于lstlst的元素。

    则显然,第1p1\sim p个元素的值都应是整块修改所赋的值,而第p+1Sizep+1\sim Size个元素都是按自己最后一次赋值所赋的值。

    不难发现,前面一部分即为pp*整块最后一次修改所赋的值,后面一部分即为respres_p

    这样就能更新ansans了。


    块内撤销操作

    首先我们要注意到一个性质:在每次重构之前,所撤销的操作的编号肯定是单调递减的。

    因此我们就可以使用之前重构时保留下来的lstlstpp两个变量,继续进行操作。

    我们先判断当前栈顶的元素是否大于lstlst

    如果大于lstlst,则说明在重构之后又进行过整块修改,而这次整块修改肯定是覆盖块内所有元素的。

    所以可以直接更新ansans块的大小*赋的值,然后退出函数。

    否则,我们就要将pp向前移,和之前的操作差不多刚好相反,找到最后一个单个元素赋值时间小于当前堆顶的元素。

    然后按照上面的方法,更新ansanspp*整块最后一次修改所赋的值+resp+res_p


    代码

    #include<bits/stdc++.h>
    #define N 100000
    #define SqrtN 400
    #define LL long long
    using namespace std;
    LL n,a[N+5],ql[N+5],qr[N+5],qv[N+5],flag[N+5];
    class Class_FIO
    {
        private:
            #define Fsize 100000
            #define tc() (A==B&&(B=(A=Fin)+fread(Fin,1,Fsize,stdin),A==B)?EOF:*A++)
            #define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,Fsize,stdout),Fout[(FoutSize=0)++]=ch))
            #define Ftemp template<typename I>
            int Top,FoutSize;char ch,*A,*B,Fin[Fsize],Fout[Fsize],Stack[Fsize];
        public:
            Class_FIO() {A=B=Fin;}
            Ftemp inline void read(I &x) {x=0;while(!isdigit(ch=tc()));while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));}
            Ftemp inline void writeln(I x) {while(Stack[++Top]=x%10+48,x/=10);while(Top) pc(Stack[Top--]);pc('\n');}
            inline void clear() {fwrite(Fout,1,FoutSize,stdout),FoutSize=0;}
    }F;
    class Class_List//邻接表,存储对每个元素的赋值操作
    {
        private:
            #define LIST_SIZE N*SqrtN
            LL cnt,lnk[N+5],val[LIST_SIZE+5],nxt[LIST_SIZE+5];
        public:
            inline void Add(LL x,LL v) {nxt[++cnt]=lnk[x],val[lnk[x]=cnt]=v;}
            inline void Del(LL x) {while(lnk[x]&&flag[val[lnk[x]]]) lnk[x]=nxt[lnk[x]];}
            inline LL GetVal(LL x) {return val[lnk[x]];}
    }List;
    class Class_BlockSolver//存储每个块的信息
    {
        private:
            #define BLOCK_SIZE SqrtN
            LL Bsize,Btot,bl[N+5];
            class Class_Block
            {
                private:
                    #define BLOCK SqrtN
                    LL p,ans,lst,Top,res[BLOCK+5],InitVal[BLOCK+5],s[BLOCK+5],g[BLOCK+5],cnt[256],Stack[N+5];
                    inline LL RadixSort()//基数排序
                    {
                        register LL i,res=0;
                        for(i=1;i<=S;++i) !(s[i]=List.GetVal(L+i-1))&&(res+=InitVal[i]);
                        for(i=0;i<256;++i) cnt[i]=0;for(i=1;i<=S;++i) ++cnt[s[i]&255];for(i=1;i<256;++i) cnt[i]+=cnt[i-1];for(i=S;i;--i) g[cnt[s[i]&255]--]=s[i];
                        for(i=0;i<256;++i) cnt[i]=0;for(i=1;i<=S;++i) ++cnt[s[i]>>8];for(i=1;i<256;++i) cnt[i]+=cnt[i-1];for(i=S;i;--i) s[cnt[g[i]>>8]--]=g[i];
                        return res;
                    }
                public:
                    LL L,R,S;
                    inline void Build(LL *data) {for(register LL i=1;i<=S;++i) res[0]+=(InitVal[i]=a[L+i-1]);ans=res[0];}//初始化
                    inline void ReBuild()//重构
                    {
                        register LL i,sum=RadixSort();
                        for(p=0,lst=Stack[Top],i=S-1;~i;--i) res[i]=res[i+1]+qv[s[i+1]];res[0]+=sum;
                        while(p<S&&s[p+1]<lst) ++p;ans=res[p]+1LL*p*qv[lst];
                    }
                    inline void Add(LL pos) {ans=1LL*S*qv[Stack[++Top]=pos];}//整块修改
                    inline void Del()//整块撤销操作
                    {
                        while(Top&&flag[Stack[Top]]) --Top;
                        if(Stack[Top]>lst) return (void)(ans=1LL*S*qv[Stack[Top]]);
                        while(p&&s[p]>=Stack[Top]) --p;ans=res[p]+1LL*p*qv[Stack[Top]];
                    }
                    inline LL BruteForce(LL l,LL r)//暴力求解答案
                    {
                        register LL i,res=0;
                        if(Stack[Top]) for(i=l;i<=r;++i) res+=qv[max(List.GetVal(i),Stack[Top])];
                        else for(i=l;i<=r;++i) res+=List.GetVal(i)?qv[List.GetVal(i)]:InitVal[i-L+1];
                        return res;
                    }
                    inline LL GetAns() {return ans;}//返回答案
            }blk[BLOCK_SIZE+5];
        public:
            inline void Init()//初始化
            {
                register LL i;
                for(Bsize=sqrt(n),i=1;i<=n;++i) ++blk[bl[i]=(i-1)/Bsize+1].S;
                for(Btot=bl[n],i=1;i<=Btot;++i) blk[i].L=(i-1)*Bsize+1,blk[i].R=i*Bsize;blk[Btot].R=n;
                for(i=1;i<=Btot;++i) blk[i].Build(a);
            }
            inline void Modify(LL pos)//修改
            {
                register LL i,l=ql[pos],r=qr[pos],v=qv[pos];
                if(!(bl[l]^bl[r])) {for(i=l;i<=r;++i) List.Add(i,pos);return blk[bl[l]].ReBuild();}
                for(i=blk[bl[l]].R;i>=l;--i) List.Add(i,pos);blk[bl[l]].ReBuild();
                for(i=blk[bl[r]].L;i<=r;++i) List.Add(i,pos);blk[bl[r]].ReBuild();
                for(i=bl[l]+1;i<bl[r];++i) blk[i].Add(pos);
            }
            inline void CtrlZ(LL pos)//撤销修改
            {
                register LL i,l=ql[pos],r=qr[pos],v=qv[pos];
                if(!(bl[l]^bl[r])) {for(i=l;i<=r;++i) List.Del(i);return blk[bl[l]].ReBuild();}
                for(i=blk[bl[l]].R;i>=l;--i) List.Del(i);blk[bl[l]].ReBuild();
                for(i=blk[bl[r]].L;i<=r;++i) List.Del(i);blk[bl[r]].ReBuild();
                for(i=bl[l]+1;i<bl[r];++i) blk[i].Del();
            }
            inline LL Query(LL l,LL r)//询问
            {
                if(!(bl[l]^bl[r])) return blk[bl[l]].BruteForce(l,r);
                register LL i,res=blk[bl[l]].BruteForce(l,blk[bl[l]].R)+blk[bl[r]].BruteForce(blk[bl[r]].L,r);
                for(i=bl[l]+1;i<bl[r];++i) res+=blk[i].GetAns();
                return res;
            }
    }B;
    int main()
    {
        register LL query_tot,i,cnt=0,op,x,y,ans=0;
        for(F.read(n),F.read(query_tot),i=1;i<=n;++i) F.read(a[i]);B.Init();
        while(query_tot--)
        {
            F.read(op);switch(op)
            {
                case 1:F.read(ql[++cnt]),F.read(qr[cnt]),F.read(qv[cnt]),ql[cnt]^=ans,qr[cnt]^=ans,B.Modify(cnt);break;
                case 2:F.read(x),F.read(y),F.writeln(ans=B.Query(x^ans,y^ans));break;
                case 3:F.read(x),flag[x^=ans]=1,B.CtrlZ(x);break;
            }
        }
        return F.clear(),0;
    }
    
    • 1

    信息

    ID
    4096
    时间
    1500ms
    内存
    500MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者