BZOJ3337 ORZJRY I

BZOJ3337 ORZJRY I

题目传送门

题意

emm...大致就是那样了吧,似乎就是一个毒瘤的数据结构来维护这么多种操作吧..

题解

观察一下这些操作,发现大部分的操作都可以通过分块的方法弄过去,但是由于有插入操作,会使得这个操作的复杂度达到\(O(n)\)的级别,所以我们不能过单独的用分块的方法去解决这道题目。但是我们会发现实际上只有这个操作限制了分块的方法,所以我们很自然的想到链表了。链表的插入的复杂度为\(O(1)\),但是其余操作的复杂度都不能满足我们的要求。于是我们就可以将这两个数据结构结合起来,就有了插入复杂度为\(O(\sqrt{n})\),查询的复杂度也为\(O(\sqrt{n})\)的块状链表。

块状链表的大致写法简单的来说,就是\(list\)\(vector\),但实际上只用\(STL\)的这两个库实际上很难满足我们所需要的操作,所以我们一般手写各种块状链表的操作。

我们的大致思路是用块状链表维护一个原序列以及一个排序之后的序列,所以结构体是这样定义的:

struct node {
    int srt[Sq+5],tmp[Sq+5];//srt表示排序之后的数组,tmp表示原数组
    int sz,Rev,Toval,Add;//sz表示块内元素个数,Rev是翻转标记,Tovakl是整个块的赋值标记,Add是整个块的加减操作
    int nxt;//nxt表示下一个块的下表
    ll sum;//sum表示块内元素和
}t[5005];

然后是一些辅助操作:
\(Find(pos,now)\):帮助我们找到第\(pos\)个元素是在哪个块内的第几个,用于其他的操作。

void Find(int &pos,int &now) {
    for (now=0;t[now].nxt!=-1&&pos>t[now].sz;now=t[now].nxt)
        pos-=t[now].sz;
}

\(Init()\):定义起始块,以及初始化内存池。

void Init() {for(int i=1;i<=5000;i++) Q.push(i);t[0].nxt=-1,t[0].sz=0;}

\(Clear(o)\)\(Del(o)\):清空一个块并把这个块回收。

void Clear(int o) {t[o].Rev=t[o].Toval=t[o].Add=t[o].sz=0;}
void Del(int o) {Q.push(o);Clear(o);}

\(Newnode()\):新建一个块,返回其下标。

int Newnode() {int id=Q.front();Q.pop();return id;}

\(Pushdown(o)\):将这个块内的各种标记下传。

void Pushdown(int o) {
    if(t[o].Rev) {reverse(t[o].tmp+1,t[o].tmp+1+t[o].sz);t[o].Rev=0;}
    if(t[o].Toval) {for(int i=1;i<=t[o].sz;i++) t[o].tmp[i]=t[o].Toval;t[o].sum=1ll*t[o].Toval*t[o].sz;t[o].Toval=0;}
    if(t[o].Add) {
        for(int i=1;i<=t[o].sz;i++) t[o].tmp[i]+=t[o].Add;
        t[o].sum+=1ll*t[o].Add*t[o].sz;
        t[o].Add=0;
    }
}

\(Update(o)\):用于一个块改变的时候,更新其\(sum\)值以及\(srt\)数组。

void Update(int o) {
    t[o].sum=0;
    for(int i=1;i<=t[o].sz;i++) {
        t[o].srt[i]=t[o].tmp[i];
        t[o].sum+=t[o].tmp[i];
    }
    sort(t[o].srt+1,t[o].srt+1+t[o].sz);
}

\(GetLR(l,r,lpos,rpos)\):在原来的块状链表中,将\([l,r]\)区间内的元素变成若干个块并返回这些块的左右下标(注意左下标的\(nxt\)才是真正的区间坐下标,至于为什么要返回这个值而不是它的\(nxt\),只是为了之后的操作方便一些)。

void GetLR(int l,int r,int &lpos,int &rpos) {
    int pos=l;
    Find(pos,lpos);Split(lpos,pos);
    pos=r+1;
    Find(pos,rpos);Split(rpos,pos);
    pos=r;
    Find(pos,rpos);
}

最核心的操作就是\(Split(o,pos)\),表示将第\(o\)个块内的\(pos\)及以后的元素分裂成为一个新的块,以方便之后的操作。由于块状链表的块个数为\(\sqrt{n}\),每个块内的大小为\(\sqrt{n}\),所以复杂度也是\(O(\sqrt{n})\)的。

void Split(int o,int pos) {
    Pushdown(o);//分裂之前先下传标记
    int Nxt=Newnode();
    for(int i=pos;i<=t[o].sz;i++) {
        t[Nxt].tmp[++t[Nxt].sz]=t[o].tmp[i];
    }
    t[o].sz=max(0,pos-1);
    t[Nxt].nxt=t[o].nxt;
    t[o].nxt=Nxt;
    Update(o);Update(Nxt);//分裂之后更新块的信息
}

然后是合并操作\(Merge(o)\),表示将第\(o\)个块与第\(o+1\)个块合并在一起。由于我们有了分裂操作,所以如果没有合并的操作,那么块的个数会变的很多,导致复杂度退化,就不能很好的实现各种操作了。所以我们需要增加\(Merge\)操作,让一些块的大小之和不满\(\sqrt{n}\)的块合并在一起,提高效率。这个实际上有点类似于分块中的定期重构,只不过这个重构的复杂度实际上很小罢了。

void Merge(int o) {
    int Nxt=t[o].nxt;
    Pushdown(o);Pushdown(Nxt);//合并之前先下传标记
    for(int i=1;i<=t[Nxt].sz;i++) {
        t[o].tmp[++t[o].sz]=t[Nxt].tmp[i];
    }
    t[o].nxt=t[Nxt].nxt;Del(Nxt);
    Update(o);//合并之后更新块的信息
}
void Union(int o) {
    for(;o!=-1;o=t[o].nxt) {
        if((~t[o].nxt)&&t[o].sz+t[t[o].nxt].sz<=Blocksz) Merge(o);//如果两个块大小之和比限定的块大小要小,就合并起来
    }
}

第一个操作,是比较简单的,我们只需要找到需要插入元素的位置,然后将所在的块分裂之后,在前一个块的末尾插入这个元素即可。在\(srt\)数组中插入,只需要找到最后一个比它小的位置,然后插入即可。实际上这可以用二分位置进行插入的,但是实际手写了之后发现并没有多大的优化,也就没有加进去了。

void Insert(int pos,int x) {
    int now;pos++;
    Find(pos,now);Split(now,pos);//找到第pos+1的位置,并分裂成一个新的块
    t[now].tmp[++t[now].sz]=x;//在第一个块的末尾插入新元素
    t[now].sum+=x;
    int p;
    for (p=1;p<t[now].sz;p++)
        if (t[now].srt[p]>x) break;
    for (int u=t[now].sz;u>p;u--)
        t[now].srt[u]=t[now].srt[u-1];//在srt数组中插入
    t[now].srt[p]=x;
    Union(now);//插入之后看是否有可以合并的块
}

第二个操作和第一个操作类似,就不多说了。

void Delete(int pos) {
    int o;
    Find(pos,o);Pushdown(o);
    for(int i=pos+1;i<=t[o].sz;i++) {
        t[o].tmp[i-1]=t[o].tmp[i];
    }
    t[o].sz--;Update(o);
    Union(o);
}

第三个操作也是比较简单的,我们先把要翻转的区间单独的弄出来,然后对于块与块之间,只需要将链表的顺序改一下就行了,对于块内,只需要打一个翻转的标记就行了,下次下传即可。

int temp[Sq*100];//temp为辅助数组
void Reverse(int l,int r) {
    int lpos=0,rpos=0;
    GetLR(l,r,lpos,rpos);//将区间单独弄出来
    int tot=0;
    for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {//注意t[lpos].nxt才是区间的起始下标
        t[i].Rev^=1;temp[++tot]=i;//给每个块打翻转标记
    }
    t[temp[1]].nxt=t[rpos].nxt;
    for(int i=tot;i>1;i--) t[temp[i]].nxt=temp[i-1];//给链表改顺序
    t[lpos].nxt=rpos;
    Union(lpos);
}

第四个操作与第二个操作实际上差不多,移动\(k\)次的实质实际上就是将前\(k\)个数与后几个数交换一下位置。所以还是先把这个区间单独的弄出来,改变一下链表的顺序就行了。

void Move(int l,int r,int k) {
    int lpos=0,rpos=0;
    int pos1=0,pos2=0;
    GetLR(l,r-k,lpos,pos1);
    GetLR(r-k+1,r,pos1,rpos);
    pos2=t[lpos].nxt;
    t[lpos].nxt=t[pos1].nxt;
    t[pos1].nxt=t[rpos].nxt;
    t[rpos].nxt=pos2;
    Union(pos2);
}

第五个操作和第六个操作就不用多说了,弄出来打个标记即可。

void Addval(int l,int r,int v) {
    int lpos=0,rpos=0;
    GetLR(l,r,lpos,rpos);
    for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
        t[i].Add+=v;
        t[i].sum+=1ll*v*t[i].sz;
    }
    Union(lpos);
}
void Sameval(int l,int r,int v) {
    int lpos=0,rpos=0;
    GetLR(l,r,lpos,rpos);
    for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
        t[i].Add=0,t[i].Toval=v;//注意区间赋值了之后就需要把区间加的标记清空
        t[i].sum=1ll*t[i].Toval*t[i].sz;
    }
    Union(lpos);
} 

第七个操作也是就是正常的操作,找到那些块,把\(sum\)加起来即可。

ll Sum(int l,int r) {
    int lpos=0,rpos=0;
    GetLR(l,r,lpos,rpos);
    ll ans=0;
    for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) ans+=t[i].sum;
    Union(lpos);
    return ans;
}

第八个操作需要注意一些细节,由于我们已经记了每一个块排好序之后的数组,所以我们把这些块找出来之后更新\(Mx\)\(Mn\)的值就行了。但是要注意的是,我们在\(GetLR\)函数中,实际上只对\(lpos,rpos\)这两个块进行了\(Split\)的操作,也就是说,这中间的块的标记都没有下传,所以我们需要注意标记对于答案的影响。后面的询问操作也大多都是这样,需要注意这一点。

int Query1(int l,int r) {
    int lpos=0,rpos=0;
    GetLR(l,r,lpos,rpos);
    int Mn=INT_MX,Mx=-INT_MX;
    for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
        if(!t[i].sz) continue;
        if(!t[i].Toval) {
            Mn=min(Mn,t[i].srt[1]+t[i].Add);Mx=max(Mx,t[i].srt[t[i].sz]+t[i].Add);
        }
        else {
            Mn=min(Mn,t[i].Toval+t[i].Add);Mx=max(Mx,t[i].Toval+t[i].Add);
        }
    }
    Union(lpos);
    return Mx-Mn;
}

第九个操作应该不用多说了,大致思路就是那样,注意细节即可。

int Query2(int l,int r,int v) {
    int lpos=0,rpos=0;
    GetLR(l,r,lpos,rpos);
    int res=INT_MX;
    for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
        if(!t[i].Toval) {
            int pos=lower_bound(t[i].srt+1,t[i].srt+1+t[i].sz,v-t[i].Add)-t[i].srt;
            if(pos!=t[i].sz+1) {
                res=min(res,abs(t[i].srt[pos]+t[i].Add-v));
            }
            if(pos!=1) {
                pos--;
                res=min(res,abs(t[i].srt[pos]+t[i].Add-v));
            }
        }
        else {
            res=min(res,abs(v-t[i].Toval-t[i].Add));
        }
    }
    Union(lpos);
    return res;
}

对于第十个操作,我们只需要二分答案,然后进行\(Check\)即可。其实这个复杂度实际上是\(O(\sqrt{n}log(n))\)的,所以实际上很卡时间。

int Query3(int l,int r,int k) {
    int lpos=0,rpos=0;
    GetLR(l,r,lpos,rpos);
    int L=0,R=INT_MX,ans=-1;
    while(L<=R) {
        int mid=(1ll*L+1ll*R)>>1;
        int tot=1;
        for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
            if(t[i].Toval) {
                if(t[i].Toval+t[i].Add<mid) tot+=t[i].sz;
            }
            else {
                int pos=upper_bound(t[i].srt+1,t[i].srt+1+t[i].sz,mid-t[i].Add-1)-t[i].srt;
                tot+=max(0,pos-1);
            }
        }
        if(k>=tot) L=mid+1,ans=mid;
        else R=mid-1;
    }
    Union(lpos);
    return ans;
}

十一个操作应该不用多说了,差不多把第十个操作的统计个数的那一段拉下来就行了。

int Query4(int l,int r,int v) {
        int lpos=0,rpos=0;
        GetLR(l,r,lpos,rpos);
        int tot=0;
        for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
            if(t[i].Toval) {
                if(t[i].Toval+t[i].Add<v) tot+=t[i].sz;
            }
            else {
                int pos=upper_bound(t[i].srt+1,t[i].srt+1+t[i].sz,v-t[i].Add-1)-t[i].srt;
                tot+=pos-1;
            }
        }
        Union(lpos);
        return tot;
    }

然后我们就会发现这道题目被我们愉悦地写了出来了。。然而并没有结束,100000跑复杂度是\(O(n\sqrt{n}log(n))\)的算法,而且各种操作的常数还挺大的,再加上人傻自带大常数,本地都要跑8~9秒,然而交到BZOJ的机子上竟让\(29888ms\)卡过去了。虽然之后一模一样的代码再也没有过去过,但是至少自己一遍过了这题啊(逃。。然后开始漫漫的卡常路,也就只能卡到\(25s\)左右。果然还是太菜了啊。这辈子都不想卡常了,还是颓数据结构比较舒服。。

Code:(加了FastIO略长。。)

#pragma GCC optimize (2,"inline")
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
namespace fastIO{
    #define BUF_SIZE 100005
    #define OUT_SIZE 100005
    #define ll long long
    //fread->read
    bool IOerror=0;
    inline char nc(){
        static char buf[BUF_SIZE],*p1=buf+BUF_SIZE,*pend=buf+BUF_SIZE;
        if (p1==pend){
            p1=buf; pend=buf+fread(buf,1,BUF_SIZE,stdin);
            if (pend==p1){IOerror=1;return -1;}
            //{printf("IO error!\n");system("pause");for (;;);exit(0);}
        }
        return *p1++;
    }
    inline bool blank(char ch){return ch==' '||ch=='\n'||ch=='\r'||ch=='\t';}
    inline void read(int &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        if (ch=='-')sign=1,ch=nc();
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
        if (sign)x=-x;
    }
    inline void read(ll &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        if (ch=='-')sign=1,ch=nc();
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
        if (sign)x=-x;
    }
    inline void read(double &x){
        bool sign=0; char ch=nc(); x=0;
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        if (ch=='-')sign=1,ch=nc();
        for (;ch>='0'&&ch<='9';ch=nc())x=x*10+ch-'0';
        if (ch=='.'){
            double tmp=1; ch=nc();
            for (;ch>='0'&&ch<='9';ch=nc())tmp/=10.0,x+=tmp*(ch-'0');
        }
        if (sign)x=-x;
    }
    inline void read(char *s){
        char ch=nc();
        for (;blank(ch);ch=nc());
        if (IOerror)return;
        for (;!blank(ch)&&!IOerror;ch=nc())*s++=ch;
        *s=0;
    }
    inline void read(char &c){
        for (c=nc();blank(c);c=nc());
        if (IOerror){c=-1;return;}
    }
    //getchar->read
    inline void read1(int &x){
        char ch;int bo=0;x=0;
        for (ch=getchar();ch<'0'||ch>'9';ch=getchar())if (ch=='-')bo=1;
        for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
        if (bo)x=-x;
    }
    inline void read1(ll &x){
        char ch;int bo=0;x=0;
        for (ch=getchar();ch<'0'||ch>'9';ch=getchar())if (ch=='-')bo=1;
        for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
        if (bo)x=-x;
    }
    inline void read1(double &x){
        char ch;int bo=0;x=0;
        for (ch=getchar();ch<'0'||ch>'9';ch=getchar())if (ch=='-')bo=1;
        for (;ch>='0'&&ch<='9';x=x*10+ch-'0',ch=getchar());
        if (ch=='.'){
            double tmp=1;
            for (ch=getchar();ch>='0'&&ch<='9';tmp/=10.0,x+=tmp*(ch-'0'),ch=getchar());
        }
        if (bo)x=-x;
    }
    inline void read1(char *s){
        char ch=getchar();
        for (;blank(ch);ch=getchar());
        for (;!blank(ch);ch=getchar())*s++=ch;
        *s=0;
    }
    inline void read1(char &c){for (c=getchar();blank(c);c=getchar());}
    //scanf->read
    inline void read2(int &x){scanf("%d",&x);}
    inline void read2(ll &x){
        #ifdef _WIN32
            scanf("%I64d",&x);
        #else
        #ifdef __linux
            scanf("%lld",&x);
        #else
            puts("error:can??t recognize the system!");
        #endif
        #endif
    }
    inline void read2(double &x){scanf("%lf",&x);}
    inline void read2(char *s){scanf("%s",s);}
    inline void read2(char &c){scanf(" %c",&c);}
    inline void readln2(char *s){gets(s);}
    //fwrite->write
    struct Ostream_fwrite{
        char *buf,*p1,*pend;
        Ostream_fwrite(){buf=new char[BUF_SIZE];p1=buf;pend=buf+BUF_SIZE;}
        void out(char ch){
            if (p1==pend){
                fwrite(buf,1,BUF_SIZE,stdout);p1=buf;
            }
            *p1++=ch;
        }
        void print(int x){
            static char s[15],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1);
        }
        void println(int x){
            static char s[15],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1); out('\n');
        }
        void print(ll x){
            static char s[25],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1);
        }
        void println(ll x){
            static char s[25],*s1;s1=s;
            if (!x)*s1++='0';if (x<0)out('-'),x=-x;
            while(x)*s1++=x%10+'0',x/=10;
            while(s1--!=s)out(*s1); out('\n');
        }
        void print(double x,int y){
            static ll mul[]={1,10,100,1000,10000,100000,1000000,10000000,100000000,
                1000000000,10000000000LL,100000000000LL,1000000000000LL,10000000000000LL,
                100000000000000LL,1000000000000000LL,10000000000000000LL,100000000000000000LL};
            if (x<-1e-12)out('-'),x=-x;x*=mul[y];
            ll x1=(ll)floor(x); if (x-floor(x)>=0.5)++x1;
            ll x2=x1/mul[y],x3=x1-x2*mul[y]; print(x2);
            if (y>0){out('.'); for (size_t i=1;i<y&&x3*mul[i]<mul[y];out('0'),++i); print(x3);}
        }
        void println(double x,int y){print(x,y);out('\n');}
        void print(char *s){while (*s)out(*s++);}
        void println(char *s){while (*s)out(*s++);out('\n');}
        void flush(){if (p1!=buf){fwrite(buf,1,p1-buf,stdout);p1=buf;}}
        ~Ostream_fwrite(){flush();}
    }Ostream;
    inline void print(int x){Ostream.print(x);}
    inline void println(int x){Ostream.println(x);}
    inline void print(char x){Ostream.out(x);}
    inline void println(char x){Ostream.out(x);Ostream.out('\n');}
    inline void print(ll x){Ostream.print(x);}
    inline void println(ll x){Ostream.println(x);}
    inline void print(double x,int y){Ostream.print(x,y);}
    inline void println(double x,int y){Ostream.println(x,y);}
    inline void print(char *s){Ostream.print(s);}
    inline void println(char *s){Ostream.println(s);}
    inline void println(){Ostream.out('\n');}
    inline void flush(){Ostream.flush();}
    //puts->write
    char Out[OUT_SIZE],*o=Out;
    inline void print1(int x){
        static char buf[15];
        char *p1=buf;if (!x)*p1++='0';if (x<0)*o++='-',x=-x;
        while(x)*p1++=x%10+'0',x/=10;
        while(p1--!=buf)*o++=*p1;
    }
    inline void println1(int x){print1(x);*o++='\n';}
    inline void print1(ll x){
        static char buf[25];
        char *p1=buf;if (!x)*p1++='0';if (x<0)*o++='-',x=-x;
        while(x)*p1++=x%10+'0',x/=10;
        while(p1--!=buf)*o++=*p1;
    }
    inline void println1(ll x){print1(x);*o++='\n';}
    inline void print1(char c){*o++=c;}
    inline void println1(char c){*o++=c;*o++='\n';}
    inline void print1(char *s){while (*s)*o++=*s++;}
    inline void println1(char *s){print1(s);*o++='\n';}
    inline void println1(){*o++='\n';}
    inline void flush1(){if (o!=Out){if (*(o-1)=='\n')*--o=0;puts(Out);}}
    struct puts_write{
        ~puts_write(){flush1();}
    }_puts;
    inline void print2(int x){printf("%d",x);}
    inline void println2(int x){printf("%d\n",x);}
    inline void print2(char x){printf("%c",x);}
    inline void println2(char x){printf("%c\n",x);}
    inline void print2(ll x){
        #ifdef _WIN32
            printf("%I64d",x);
        #else
        #ifdef __linux
            printf("%lld",x);
        #else
            puts("error:can??t recognize the system!");
        #endif
        #endif
    }
    inline void println2(ll x){print2(x);printf("\n");}
    inline void println2(){printf("\n");}
    #undef ll
    #undef OUT_SIZE
    #undef BUF_SIZE
};
using namespace fastIO;
/*================Header Template==============*/
const int Sq=405;
const int INT_MX=214748347;
int Blocksz;
int n,q;
/*==================Define Area================*/
namespace BlockList {
    struct node {
        int srt[Sq+5],tmp[Sq+5];
        int sz,Rev,Toval,Add;
        int nxt;
        ll sum;
    }t[5005];
    queue<int>Q;
    void Clear(int o) {t[o].Rev=t[o].Toval=t[o].Add=t[o].sz=0;}
    void Del(int o) {Q.push(o);Clear(o);}
    int Newnode() {int id=Q.front();Q.pop();return id;}
    void Init() {for(int i=1;i<=5000;i++) Q.push(i);t[0].nxt=-1,t[0].sz=0;}
    void Pushdown(int o) {
        if(t[o].Rev) {reverse(t[o].tmp+1,t[o].tmp+1+t[o].sz);t[o].Rev=0;}
        if(t[o].Toval) {for(int i=1;i<=t[o].sz;i++) t[o].tmp[i]=t[o].Toval;t[o].sum=1ll*t[o].Toval*t[o].sz;t[o].Toval=0;}
        if(t[o].Add) {
            for(int i=1;i<=t[o].sz;i++) t[o].tmp[i]+=t[o].Add;
            t[o].sum+=1ll*t[o].Add*t[o].sz;
            t[o].Add=0;
        }
    }
    void Update(int o) {
        t[o].sum=0;
        for(int i=1;i<=t[o].sz;i++) {
            t[o].srt[i]=t[o].tmp[i];
            t[o].sum+=t[o].tmp[i];
        }
        sort(t[o].srt+1,t[o].srt+1+t[o].sz);
    }
    void Find(int &pos,int &now) {
        for (now=0;t[now].nxt!=-1&&pos>t[now].sz;now=t[now].nxt)
            pos-=t[now].sz;
    }
    void Split(int o,int pos) {
        Pushdown(o);
        int Nxt=Newnode();
        for(int i=pos;i<=t[o].sz;i++) {
            t[Nxt].tmp[++t[Nxt].sz]=t[o].tmp[i];
        }
        t[o].sz=max(0,pos-1);
        t[Nxt].nxt=t[o].nxt;
        t[o].nxt=Nxt;
        Update(o);Update(Nxt);
    }
    void Merge(int o) {
        int Nxt=t[o].nxt;
        Pushdown(o);Pushdown(Nxt);
        for(int i=1;i<=t[Nxt].sz;i++) {
            t[o].tmp[++t[o].sz]=t[Nxt].tmp[i];
        }
        t[o].nxt=t[Nxt].nxt;Del(Nxt);
        Update(o);
    }
    void Union(int o) {
        for(;o!=-1;o=t[o].nxt) {
            if((~t[o].nxt)&&t[o].sz+t[t[o].nxt].sz<=Blocksz) Merge(o);
        }
    }
    void Insert(int pos,int x) {
        int now;pos++;
        Find(pos,now);Split(now,pos);
        t[now].tmp[++t[now].sz]=x;
        t[now].sum+=x;
        int p;
        for (p=1;p<t[now].sz;p++)
            if (t[now].srt[p]>x) break;
        for (int u=t[now].sz;u>p;u--)
            t[now].srt[u]=t[now].srt[u-1];
        t[now].srt[p]=x;
        Union(now);
    }
    void Delete(int pos) {
        int o;
        Find(pos,o);Pushdown(o);
        for(int i=pos+1;i<=t[o].sz;i++) {
            t[o].tmp[i-1]=t[o].tmp[i];
        }
        t[o].sz--;Update(o);
        Union(o);
    }
    void GetLR(int l,int r,int &lpos,int &rpos) {
        int pos=l;
        Find(pos,lpos);Split(lpos,pos);
        pos=r+1;
        Find(pos,rpos);Split(rpos,pos);
        pos=r;
        Find(pos,rpos);
    }
    int temp[Sq*100];
    void Reverse(int l,int r) {
        int lpos=0,rpos=0;
        GetLR(l,r,lpos,rpos);
        int tot=0;
        for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
            t[i].Rev^=1;temp[++tot]=i;
        }
        t[temp[1]].nxt=t[rpos].nxt;
        for(int i=tot;i>1;i--) t[temp[i]].nxt=temp[i-1];
        t[lpos].nxt=rpos;
        Union(lpos);
    }
    void Move(int l,int r,int k) {
        int lpos=0,rpos=0;
        int pos1=0,pos2=0;
        GetLR(l,r-k,lpos,pos1);
        GetLR(r-k+1,r,pos1,rpos);
        pos2=t[lpos].nxt;
        t[lpos].nxt=t[pos1].nxt;
        t[pos1].nxt=t[rpos].nxt;
        t[rpos].nxt=pos2;
        Union(pos2);
    }
    void Addval(int l,int r,int v) {
        int lpos=0,rpos=0;
        GetLR(l,r,lpos,rpos);
        for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
            t[i].Add+=v;
            t[i].sum+=1ll*v*t[i].sz;
        }
        Union(lpos);
    }
    void Sameval(int l,int r,int v) {
        int lpos=0,rpos=0;
        GetLR(l,r,lpos,rpos);
        for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
            t[i].Add=0,t[i].Toval=v;
            t[i].sum=1ll*t[i].Toval*t[i].sz;
        }
        Union(lpos);
    } 
    ll Sum(int l,int r) {
        int lpos=0,rpos=0;
        GetLR(l,r,lpos,rpos);
        ll ans=0;
        for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) ans+=t[i].sum;
        Union(lpos);
        return ans;
    }
    int Query1(int l,int r) {
        int lpos=0,rpos=0;
        GetLR(l,r,lpos,rpos);
        int Mn=INT_MX,Mx=-(INT_MX-2);
        for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
            if(!t[i].sz) continue;
            if(!t[i].Toval) {
                Mn=min(Mn,t[i].srt[1]+t[i].Add);Mx=max(Mx,t[i].srt[t[i].sz]+t[i].Add);
            }
            else {
                Mn=min(Mn,t[i].Toval+t[i].Add);Mx=max(Mx,t[i].Toval+t[i].Add);
            }
        }
        Union(lpos);
        return Mx-Mn;
    }
    int Query2(int l,int r,int v) {
        int lpos=0,rpos=0;
        GetLR(l,r,lpos,rpos);
        int res=INT_MX;
        for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
            if(!t[i].Toval) {
                int pos=lower_bound(t[i].srt+1,t[i].srt+1+t[i].sz,v-t[i].Add)-t[i].srt;
                if(pos!=t[i].sz+1) {
                    res=min(res,abs(t[i].srt[pos]+t[i].Add-v));
                }
                if(pos!=1) {
                    pos--;
                    res=min(res,abs(t[i].srt[pos]+t[i].Add-v));
                }
            }
            else {
                res=min(res,abs(v-t[i].Toval-t[i].Add));
            }
        }
        Union(lpos);
        return res;
    }
    int Query3(int l,int r,int k) {
        int lpos=0,rpos=0;
        GetLR(l,r,lpos,rpos);
        int L=0,R=INT_MX,ans=-1;
        while(L<=R) {
            int mid=(1ll*L+1ll*R)>>1;
            int tot=1;
            for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
                if(t[i].Toval) {
                    if(t[i].Toval+t[i].Add<mid) tot+=t[i].sz;
                }
                else {
                    int pos=upper_bound(t[i].srt+1,t[i].srt+1+t[i].sz,mid-t[i].Add-1)-t[i].srt;
                    tot+=max(0,pos-1);
                }
            }
            if(k>=tot) L=mid+1,ans=mid;
            else R=mid-1;
        }
        Union(lpos);
        return ans;
    }
    int Query4(int l,int r,int v) {
        int lpos=0,rpos=0;
        GetLR(l,r,lpos,rpos);
        int tot=0;
        for(int i=t[lpos].nxt;i!=t[rpos].nxt;i=t[i].nxt) {
            if(t[i].Toval) {
                if(t[i].Toval+t[i].Add<v) tot+=t[i].sz;
            }
            else {
                int pos=upper_bound(t[i].srt+1,t[i].srt+1+t[i].sz,v-t[i].Add-1)-t[i].srt;
                tot+=pos-1;
            }
        }
        Union(lpos);
        return tot;
    }
}
  
using namespace BlockList;
 
int main() {
     // freopen("1.in","r",stdin);
     // freopen("my.out","w",stdout);
    read(n);
     // int t=clock();
    Blocksz=sqrt(n)+65;
    Init();
    // cerr<<"GG"<<endl;
    for(int i=1,x;i<=n;i++) {
        // printf("%d ",i);
        read(x);
        Insert(i-1,x);
        // Debug();
    }
    // int tt=clock();
    // cerr<<"Read time:"<<tt-t<<"ms"<<endl;
    // puts("");
    // cerr<<"GG2"<<endl;
    read(q);
    while(q--) {
        // if(q%1000==0)cerr<<q<<endl;
        int tpe;
        read(tpe);
        if(tpe==1) {
            int x,v;
            read(x);read(v);
            Insert(x,v);
        }
        if(tpe==2) {
            int x;
            read(x);
            Delete(x);
        }
        if(tpe==3) {
            int l,r;
            read(l);read(r);
            Reverse(l,r);
        }
        if(tpe==4) {
            int l,r,k;
            read(l);read(r);read(k);
            Move(l,r,k);
        }
        if(tpe==5) {
            int l,r,v;
            read(l);read(r);read(v);
            Addval(l,r,v);
        }
        if(tpe==6) {
            int l,r,v;
            read(l);read(r);read(v);
            Sameval(l,r,v);
        }
        if(tpe==7) {
            int l,r;
            read(l);read(r);
            ll res=Sum(l,r);
            // puts("Query Sum:");
            println(res);
        }
        if(tpe==8) {
            int l,r;
            read(l);read(r);
            int res=Query1(l,r);
            // puts("Query dif:");
            println(res);
        }
        if(tpe==9) {
            int l,r,v;
            read(l);read(r);read(v);
            int res=Query2(l,r,v);
            // puts("Query Min:");
            println(res);
        }
        if(tpe==10) {
            int l,r,k;
            read(l);read(r);read(k);
            int res=Query3(l,r,k);
            // puts("Query k:");
            println(res);
        }
        if(tpe==11) {
            int l,r,v;
            read(l);read(r);read(v);
            int res=Query4(l,r,v);
            // puts("Query cnt:");
            println(res);
        }
        // Debug();
    }
    // int ttt=clock();
    // cerr<<"Do time:"<<ttt-t<<"ms"<<endl;
    return 0;
}
  
/*
6
5 2 6 3 1 4
15
7 2 4
8 1 3
9 2 4 5
10 1 6 4
11 2 5 4
6 1 4 7
8 1 4
5 3 4 5
2 1
1 2 8
3 3 5
4 1 5 2
9 2 5 4
10 3 6 4
11 1 6 100
*/
*/
posted @ 2018-10-15 20:29  Apocrypha  阅读(295)  评论(0编辑  收藏  举报