bzoj 3337: ORZJRY I

Description

Jry最近做(屠)了很多数据结构题,所以想 BS你,他希望你能实现一种数据结构维护一个序列:
这里写图片描述

Input

第一行n;
第二行n个数;
第三行q,代表询问个数;
接下来q行,每行一个op,输入格式见描述。

Output

对于7≤op≤11的操作,一行输出一个答案。
Sample Input

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

Sample Output

11

4

1

4

3

0

3

12

6

HINT

n,q≤100000;

任意时刻数列中的数≤2^31-1。

0≤任意时刻数列中的数≤2^31-1。

本题共3组数据

题解

自从做了这题,感觉世界豁然开朗了。。
感觉自己再也不怂数据结构题了
感觉自己的块状链表打开了新世界
感觉维修数列已经不再是恐怖的数据结构题了
感觉自己的码力+=MAX

全程%下面的大佬
ORZ
必须要怒%

由于抄代码,也就写了一下午就写完了

该注意的细节注释里都有了
怎么做注释里也有了
询问操作的注释可能比较少,但是相信大家也能看懂

就这样吧

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
typedef long long LL;
const int N=10000;
const int MAX=1<<29;
int n,m;
int size;//块的大小
struct node
{
    int d[1003],s[1003];//原数组   排序后的数组
    int rev,add,same,size,next;//翻转标记    区间加   区间一样   这个块的大小   链表 
    LL sum;//区间和 
}a[N];//块 
queue<int> q;//回收节点 
void Init ()
{
    for (int u=1;u<N;u++) q.push(u);
    a[0].next=-1;a[0].size=0;
}
int new_node()
{
    int temp=q.front();q.pop();
    return temp;
}
void clear (int x){a[x].rev=a[x].add=a[x].same=a[x].size=a[x].rev=0;}
void del (int x){q.push(x);clear(x);}//删除并回收这个块 
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void find (int &pos,int &now)//找到pos所在的块与排位
{
    for (now=0;a[now].next!=-1&&pos>a[now].size;now=a[now].next)
        pos-=a[now].size;
}
void push_down(int now)//将now这个块的标记都下传 
{
    int tot=a[now].size;//先得到这个块的大小 
    if (a[now].rev)
    {
        a[now].rev=false;
        int tt=(tot>>1);
        for (int u=1;u<=tt;u++) swap(a[now].d[u],a[now].d[tot-u+1]);
    }
    if (a[now].same!=0)
    {
        for (int u=1;u<=tot;u++)
            a[now].d[u]=a[now].same;
        a[now].sum=a[now].same*tot;
        a[now].same=0;
    }
    if (a[now].add!=0)
    {
        for (int u=1;u<=tot;u++)
            a[now].d[u]+=a[now].add;
        a[now].sum=a[now].sum+a[now].add*tot;
        a[now].add=0;
    }
}
void update (int x)//重构这个块 
{
    a[x].sum=0;
    for (int u=1;u<=a[x].size;u++)
        a[x].sum+=a[x].d[u],a[x].s[u]=a[x].d[u];
    sort(a[x].s+1,a[x].s+a[x].size+1);
}
void spilt (int now,int pos)//将now这一个块的后pos个分离出来
{
    push_down(now);//因为要把分离,所以要下传了 
    int t=new_node();//将这个块的pos到后面分裂 
    for (int u=pos;u<=a[now].size;u++)
        a[t].d[++a[t].size]=a[now].d[u];
    a[t].next=a[now].next;
    a[now].next=t;
    a[now].size=max(pos-1,0);//把后面的一部分裁掉
    update(t);update(now);//其实这里看起来是2sqrtn,但他们加起来其实只有sqrtn的 
}
void Merge (int now)//将now和now+1合并 
{
    int k=a[now].next;
    push_down(now);push_down(k);
    for (int u=1;u<=a[k].size;u++)
        a[now].d[++a[now].size]=a[k].d[u];
    a[now].next=a[k].next;del(k);
    update(now);
}
void maintain(int now)//看下后面有没有可以合并的 
{
    for (;now!=-1;now=a[now].next)
        if (a[now].next!=-1&&a[now].size+a[a[now].next].size<=size)//这两个小块,合并掉
            Merge(now); 
}
void ins (int pos,int x)//在pos这个位置后面插入一个值为x的点 
{
    int now;pos++;
    find(pos,now);spilt(now,pos);
    a[now].d[++a[now].size]=x;//直接插到末尾就可以了 
    /*插入排序*/
    a[now].sum+=x;
    int lalal;//寻找放在哪里 
    for (lalal=1;lalal<a[now].size;lalal++)
        if (a[now].s[lalal]>x) break;
    for (int u=a[now].size;u>lalal;u--)
        a[now].s[u]=a[now].s[u-1];
    a[now].s[lalal]=x;
    /*插入排序*/
    /*注意:这里为什么可以写插入排序呢?是因为上面的spi操作已经把s数组更新了,都得到了标记的值*/
    /*但是下面del操作的时候s数组是没有更新的,因此要重构*/
    maintain(now);
}
void Del (int pos)//删除第x个数 
{
    int now;
    find(pos,now);push_down(now);
    for (int u=pos+1;u<=a[now].size;u++)
        a[now].d[u-1]=a[now].d[u];
    a[now].size--;
    update(now);
    maintain(now);
}
void solve (int l,int r,int &lp,int &rp)//把这段区间分离出来 
{
    int pos=l;
    find(pos,lp);spilt(lp,pos);
    pos=r+1;
    find(pos,rp);
    spilt(rp,pos);
    pos=r;
    find(pos,rp);
}
int st[N];
void do_reverse (int l,int r)//翻转操作
{
    /*翻转过程怎么做呢:*/
    /*先把区间都分离出来,然后把他们间的顺序弄反,其实就是链表翻转*/
    /*然后要注意头和尾的链表*/
    /*然后每个块里面翻转*/
    int lp,rp;
    solve(l,r,lp,rp);
    int now=lp,top=0;
    for (int u=a[lp].next;u!=a[rp].next;u=a[u].next)
        st[++top]=u,a[u].rev^=1;
    a[st[1]].next=a[rp].next;
    for (int u=top;u>1;u--)
        a[st[u]].next=st[u-1];
    a[lp].next=rp;
    maintain(lp);
}
void do_move(int l,int r,int k)
{
    int lp,mp,rp,np;
    solve(l,r-k,lp,mp);
    solve(r-k+1,r,mp,rp);
    np=a[lp].next;//np才是l在的块
    a[lp].next=a[mp].next;
    a[mp].next=a[rp].next;
    a[rp].next=np;
    maintain(lp); 
}
void add (int l,int r,int val)
{
    int lp,rp;
    solve(l,r,lp,rp);
    for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
    {
        a[now].add+=val;
        a[now].sum=a[now].sum+a[now].size*val;
    }
    maintain(lp);
}
void same (int l,int r,int val)
{
    int lp,rp;
    solve(l,r,lp,rp);
    for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
    {
        a[now].add=0;
        a[now].same=val;
        a[now].sum=a[now].size*val;
    }
    maintain(lp);
}
LL get (int l,int r)//这一段的和
{
    int lp,rp;
    solve(l,r,lp,rp);
    LL ans=0;
    for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
        ans=ans+a[now].sum;
    maintain(lp);
    return ans;
}
int get1 (int l,int r)//这一段的最大值减最小值
{
    int lp,rp;
    solve(l,r,lp,rp);
    int maxx=-MAX,minn=MAX;
    for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
        if (a[now].size!=0)
        {
            if (a[now].same!=0)//注意,一定要判这一句  虽然看起来solve里面有update,但中间的块都是没有update的 
            {
                minn=min(minn,a[now].same+a[now].add);
                maxx=max(maxx,a[now].same+a[now].add);
            }
            else
            {
                minn=min(minn,a[now].s[1]+a[now].add);
                maxx=max(maxx,a[now].s[a[now].size]+a[now].add);
            }
        }
    maintain(lp);
    return maxx-minn;
}
int near (int l,int r,int val)
{
    int lp,rp;
    solve(l,r,lp,rp);
    int ans=MAX;
    for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
    {
        if (a[now].same)
            ans=min(ans,abs(val-a[now].same-a[now].add));
        else
        {
            int id=lower_bound(a[now].s+1,a[now].s+a[now].size+1,val-a[now].add)-a[now].s;
            if (id!=a[now].size+1)
                ans=min(ans,a[now].s[id]+a[now].add-val);
            if (id!=1)
                id--,ans=min(ans,val-a[now].s[id]-a[now].add);
        }
    }
    maintain(lp);
    return ans;
}
int ask_mink(int l,int r,int k)//l到r的第k小
{
    int lp,rp;
    solve(l,r,lp,rp);
    int ll=0,rr=MAX;
    while (ll<rr)
    {
        int mid=(ll+rr)/2+1;
        int sum=1;//看看有多少个数
        for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
        {
            if (a[now].same!=0)
            {
                if (a[now].same+a[now].add<mid)
                    sum=sum+a[now].size;
            }
            else
            {
                int id=upper_bound(a[now].s+1,a[now].s+a[now].size+1,mid-a[now].add-1)-a[now].s;  
                sum=sum+max(0,id-1);
            }
        }
        if (k>=sum) ll=mid;
        else rr=mid-1;
    }
    maintain(lp);  
    return ll;
}
int ask_smaller(int l,int r,int val)
{
    int lp,rp;
    solve(l,r,lp,rp);
    int ans=0;
    for (int now=a[lp].next;now!=a[rp].next;now=a[now].next)
    {
        if (a[now].same!=0)
        {
            if (a[now].same+a[now].add<val)
                ans=ans+a[now].size;
        }
        else
        {
            int it=upper_bound(a[now].s+1,a[now].s+a[now].size+1,val-a[now].add-1)-a[now].s;
            ans=ans+it-1;
        }
    }
    maintain(lp);
    return ans;
}
int main()
{
    n=read();
    size=sqrt(n);Init();
    for (int u=1;u<=n;u++)
    {
        int x=read();
        ins(u-1,x); 
    }

    m=read();
    for (int u=1;u<=m;u++)
    {
        int op,x,y,z;op=read();
        switch(op)
        {
            case 1:x=read();y=read();ins(x,y);break;
            case 2:x=read();Del(x);break;
            case 3:x=read();y=read();do_reverse(x,y);break;
            case 4:x=read();y=read();z=read();do_move(x,y,z);break;
            case 5:x=read();y=read();z=read();add(x,y,z);break;
            case 6:x=read();y=read();z=read();same(x,y,z);break;
            case 7:x=read();y=read();printf("%lld\n",get(x,y));break;
            case 8:x=read();y=read();printf("%d\n",get1(x,y));break;
            case 9:x=read();y=read();z=read();printf("%d\n",near(x,y,z));break;
            case 10:x=read();y=read();z=read();printf("%d\n",ask_mink(x,y,z));break;
            case 11:x=read();y=read();z=read();printf("%d\n",ask_smaller(x,y,z));break;
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值