P1110 [ZJOI2007] 报表统计

P1110 [ZJOI2007] 报表统计

题目描述

小 Q 的妈妈是一个出纳,经常需要做一些统计报表的工作。今天是妈妈的生日,小 Q 希望可以帮妈妈分担一些工作,作为她的生日礼物之一。

经过仔细观察,小 Q 发现统计一张报表实际上是维护一个非负整数数列,并且进行一些查询操作。

在最开始的时候,有一个长度为 \(n\) 的整数序列 \(a\),并且有以下三种操作:

  • INSERT i k:在原数列的第 \(i\) 个元素后面添加一个新元素 \(k\);如果原数列的第 \(i\) 个元素已经添加了若干元素,则添加在这些元素的最后(见样例说明)。
  • MIN_GAP:查询相邻两个元素的之间差值(绝对值)的最小值。
  • MIN_SORT_GAP:查询所有元素中最接近的两个元素的差值(绝对值)。

于是小 Q 写了一个程序,使得程序可以自动完成这些操作,但是他发现对于一些大的报表他的程序运行得很慢,你能帮助他改进程序么?

数据规模与约定

对于全部的测试点,保证 \(2 \le n, m \le 5\times10^5\)\(1 \leq i \leq n\)\(0 \leq a_i, k \leq 5 \times 10^8\)

说句闲话

又是一道在压在List里很久的题 (然后你就一直拖到现在才写是吧)

Solution:

不难发现,INSERT i k 简直是平衡树模板, MIN_SORT_GAP 显然是单调不升的一个东西,我们只需要在 INSERT i k 时维护一下 \(k\) 和左右邻居产生的贡献就好了。所以这题唯一不太好写的就是这个 MIN_GAP

所以我们再维护两个值,\(pre,suf\) 分别表示当前节点的子树下编号最小/最大的节点所对应的权值。那么“与x相邻”的两个树就分别是:\(t[ls].suf\),\(t[rs].pre\)。我们只需要在 pushup 的时候维护这两个东西对于节点 \(x\) 的贡献就好了。

一些细节:

由于我们的\(pre,suf\) 都不是永久化的,所以在每一次 pushup 时要先将当前节点的 \(suf,pre\) 重新赋值为 \(t[x].val\) 然后更新,否则正确性就是错的。

Code:

#include<bits/stdc++.h>
const int N=1.1e6+5;
const int inf=1e9;
using namespace std;
int n,m,flag;
int rd(){return rand()*rand()+17;}
struct FHQ_Treap{
    int cnt=0,rt;
    struct Tree{
        int ls,rs,val,siz,pre,suf,ans,pri;
    }t[N];
    int Node(int val){t[++cnt]={0,0,val,1,val,val,inf,rd()};return cnt;}
    inline void pushup(int x)
    {
        int ls=t[x].ls,rs=t[x].rs;
        t[x].siz=t[ls].siz+t[rs].siz+1;
        t[x].suf=t[x].pre=t[x].val;
        if(ls)t[x].pre=t[ls].pre;
        if(rs)t[x].suf=t[rs].suf;

        t[x].ans=inf;
        if(ls)t[x].ans=min({abs(t[ls].suf-t[x].val),t[ls].ans,t[x].ans});
        if(rs)t[x].ans=min({abs(t[rs].pre-t[x].val),t[rs].ans,t[x].ans});
    }
    void splite_siz(int x,int &a,int &b,int k)
    {
        if(!x){a=b=0;return ;}
        int tmp=t[t[x].ls].siz+1;
        if(k>=tmp){a=x;splite_siz(t[x].rs,t[x].rs,b,k-tmp);}
        else {b=x;splite_siz(t[x].ls,a,t[x].ls,k);}
        pushup(x);
    }
    void splite_val(int x,int &a,int &b,int k)
    {
        if(!x){a=b=0;return ;}
        if(k>=t[x].val){a=x;splite_val(t[x].rs,t[x].rs,b,k);}
        else {b=x;splite_val(t[x].ls,a,t[x].ls,k);}
        pushup(x);
    }
    int merge(int x,int y)
    {
        if(!x||!y)return x|y;
        if(t[x].pri<t[y].pri){t[x].rs=merge(t[x].rs,y);pushup(x);return x;}
        else {t[y].ls=merge(x,t[y].ls);pushup(y);return y;}
    }
}T1,T2;
struct Segment_Tree{
    #define ls x<<1
    #define rs x<<1|1
    struct Tree{
        int cnt;
    }t[N];
    void build(int x,int l,int r)
    {
        t[x].cnt=r-l+1;
        if(l==r)return;
        int mid=l+r>>1;
        build(ls,l,mid);build(rs,mid+1,r);
    }
    void upd(int x,int l,int r,int pos)
    {
        t[x].cnt++;
        if(l==r)return;
        int mid=l+r>>1;
        if(pos<=mid)upd(ls,l,mid,pos);
        if(mid<pos)upd(rs,mid+1,r,pos);
    }
    int query(int x,int l,int r,int L,int R)
    {
        if(L<=l&&r<=R)return t[x].cnt;
        int mid=l+r>>1,res=0;
        if(L<=mid)res+=query(ls,l,mid,L,R);
        if(mid<R)res+=query(rs,mid+1,r,L,R);
        return res;
    }
    #undef ls
    #undef rs
}T;
int ans[2]={inf,inf};
void check(int x)
{
    int a,b,c;
    T1.splite_siz(T1.rt,a,b,x);
    T1.splite_siz(b,b,c,1);
    cout<<T1.t[b].val<<"\n";
    T1.rt=T1.merge(T1.merge(a,b),c);
}
void debug()
{
    for(int i=0;i<T1.cnt;i++)check(i);
    cout<<"\n";
}
void insert(int pos,int val)
{
    int a,b,c,d;
    a=b=c=d=0;
    T1.splite_siz(T1.rt,a,c,pos);
    T1.rt=T1.merge(T1.merge(a,T1.Node(val)),c);
    ans[0]=T1.t[T1.rt].ans;
    a=b=c=d=0;
    T2.splite_val(T2.rt,a,c,val);
    T2.splite_siz(a,a,b,T2.t[a].siz-1);
    T2.splite_siz(c,c,d,1);
    if(b)ans[1]=min(ans[1],abs(T2.t[b].val-val));
    if(c)ans[1]=min(ans[1],abs(T2.t[c].val-val));
    T2.rt=T2.merge(T2.merge(T2.merge(a,b),T2.Node(val)),T2.merge(c,d));
}
char c[20];
void work()
{
    cin>>n>>m;
    for(int i=1,x;i<=n;i++)
    {
        scanf("%d",&x);
        insert(i-1,x);
    }

    T.build(1,1,n);
    for(int i=1,pos,val,tot;i<=m;i++)
    {
        scanf("%s",c);
        if(c[0]=='I')
        {
            scanf("%d%d",&pos,&val);
            tot=T.query(1,1,n,1,pos);
            insert(tot,val);
            T.upd(1,1,n,pos);
        }
        if(c[4]=='G')
        {
            printf("%d\n",T1.t[T1.rt].ans);
        }
        if(c[4]=='S')
        {
            printf("%d\n",ans[1]);
        }
    }
}
int main()
{
    //freopen("P1110_1.in","r",stdin);freopen("P1110.out","w",stdout);
    work();
    return 0;
}
posted @ 2025-01-16 18:44  liuboom  阅读(36)  评论(0)    收藏  举报