BZOJ 1058 报表统计(splay+set)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1058

题意:给出一个数列,初始时有n个数字。三种操作:(1)在第i个之后插入数字k,(1<=i<=n),注意这里的插入,这个n就是一开始的n,不是当前数列的长度。比如若在第i个之后插入数字x,又再第i个数字之后插入y,那么y要插在x的后面;(2)询问数列中相邻两项差值的最小值;(3)询问整个数列所有数中差值的最小值。

思路:(1)首先,第二种询问比较容易解决,只要将所有数字插入到set中,每次找与当前数字最近的两个比较即可;并且显然这个值只会递减不会增加;

(2)对于第一种查询我使用splay实现,节点保存当前节点的val以及以当前节点为根的子树的区间的两个端点的值Lval和Rval,以及相邻数字的最小值Min。每次更新即可。

 



struct node
{
    i64 val,Lval,Rval,Min;
    int size;
    node *p,*c[2];
};

node a[N],*nullNode,*root,*x1,*x2;
int cnt;


node *newNode(i64 val,node *p)
{
    node *e=&a[cnt++];
    e->c[0]=e->c[1]=nullNode;
    e->p=p;
    e->val=e->Lval=e->Rval=val;
    e->Min=inf;
    e->size=1;
    return e;
}


void pushUp(node *p)
{
    if(p==nullNode) return;
    p->size=1+p->c[0]->size+p->c[1]->size;


    p->Min=inf;


    if(p->c[0]!=x1&&p->c[0]!=x2&&p->c[0]!=nullNode)
    {
        p->Min=min(abs(p->val-p->c[0]->Rval),p->c[0]->Min);
        p->Lval=p->c[0]->Lval;
    }
    else p->Lval=p->val;


    if(p->c[1]!=x1&&p->c[1]!=x2&&p->c[1]!=nullNode)
    {
        p->Min=min(p->Min,min(abs(p->val-p->c[1]->Lval),p->c[1]->Min));
        p->Rval=p->c[1]->Rval;
    }
    else p->Rval=p->val;
}


void init()
{
    cnt=0;
    nullNode=0;
    nullNode=newNode(0,nullNode);
    nullNode->size=0;


    root=newNode(inf,nullNode);
    root->c[1]=newNode(inf,root);
    pushUp(root);


    x1=root;
    x2=root->c[1];
}


void zig(node *x)
{
    node *p=x->p,*q=p->p;
    p->c[0]=x->c[1];
    if(x->c[1]!=nullNode) x->c[1]->p=p;
    x->c[1]=p;
    p->p=x;
    if(q!=nullNode)
    {
        if(q->c[0]==p) q->c[0]=x;
        else q->c[1]=x;
    }
    x->p=q;
    pushUp(p);
    pushUp(x);
    if(root==p) root=x;
}

void zag(node *x)
{
    node *p=x->p,*q=p->p;
    p->c[1]=x->c[0];
    if(x->c[0]!=nullNode) x->c[0]->p=p;
    x->c[0]=p;
    p->p=x;
    if(q!=nullNode)
    {
        if(q->c[0]==p) q->c[0]=x;
        else q->c[1]=x;
    }
    x->p=q;
    pushUp(p);
    pushUp(x);
    if(root==p) root=x;
}

void splay(node *x,node *goal)
{
    while(x->p!=goal)
    {
        if(x->p->p!=goal)
        {
            if(x->p->p->c[0]==x->p)
            {
                if(x->p->c[0]==x) zig(x->p),zig(x);
                else zag(x),zig(x);
            }
            else
            {
                if(x->p->c[1]==x) zag(x->p),zag(x);
                else zig(x),zag(x);
            }
        }
        else
        {
            if(x->p->c[0]==x) zig(x);
            else zag(x);
        }
    }
}


void select(int k,node *goal)
{
    node *x=root;
    while(k!=1+x->c[0]->size)
    {
        if(k<=x->c[0]->size) x=x->c[0];
        else
        {
            k-=1+x->c[0]->size;
            x=x->c[1];
        }
    }
    splay(x,goal);
}

i64 ans1,ans2;
set<i64> s;


void insert(int k,i64 val)
{
    select(k+1,nullNode);
    select(k+2,root);
    node *p=newNode(val,root->c[1]);
    root->c[1]->c[0]=p;
    splay(root->c[1]->c[0],nullNode);


    ans1=root->Min;


    if(ans2==0) return;


    set<i64>::iterator it;
    it=s.lower_bound(val);
    if(it!=s.end())
    {
        if(abs(*it-val)<ans2) ans2=abs(*it-val);
    }
    if(it!=s.begin())
    {
        it--;
        if(abs(*it-val)<ans2) ans2=abs(*it-val);
    }
    s.insert(val);
}


int n,m;


int S[N];


void add(int x)
{
    while(x<N)
    {
        S[x]++;
        x+=x&-x;
    }
}

int get(int x)
{
    int ans=0;
    while(x)
    {
        ans+=S[x];
        x-=x&-x;
    }
    return ans;
}

int main()
{
    init(); RD(n,m); ans1=ans2=inf;
    int i;
    i64 x;
    FOR1(i,n)
    {
        RD(x);
        insert(get(i),x);
        add(i);
    }
    char op[20];
    int pos;
    i64 val;
    while(m--)
    {
        RD(op);
        if(op[0]=='I')
        {
            RD(pos);
            RD(val);
            insert(get(pos),val);
            add(pos);
        }
        else if(op[4]=='G') PR(ans1);
        else PR(ans2);


    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值