洛谷P3380:二逼平衡树 (树套树)

题目传送门:https://www.luogu.org/problemnew/show/3380


题目分析:为什么我要把这道模板题写在博客上呢?因为我想记录一个卡常的小技巧。骗访问量

这题我是考noip之前码的,写的是坐标线段树套动态开节点的权值线段树,虽然空间是 O(nlog2(n)) ,但实际空间并没有这么大。由于在 log(n) 棵线段树上同时二叉查找,时间是 O(mlog2(n)) 的,然而被卡成了70分,跑得比Ghastlcon的 log3(n) 的zkw线段树+二分+Treap还慢。

多次修改后还是T3个点,我决定弃坑了。今天早上我忽然想到一个很有力的优化:如果权值线段树已经走到空节点,就不要再往下走,直接退出。结果900多ms卡过了这题QAQ。


CODE:

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=50100;
const int maxl=21;
const int oo=2147483647;

struct Tnode
{
    int sum;
    Tnode *lson,*rson;
} tree[maxn*maxl*maxl];
Tnode *Seg[maxn<<2];
Tnode *Root[maxl<<1];
int cur=-1,num;

struct data
{
    int l,r,k,val,id,Time,opt;
} work[maxn*3];
int temp;

int a[maxn];
int b[maxn<<1];
int n,m;

bool Comp1(data x,data y)
{
    return x.val<y.val;
}

bool Comp2(data x,data y)
{
    return x.Time<y.Time;
}

Tnode *New_node()
{
    cur++;
    tree[cur].sum=0;
    tree[cur].lson=tree[cur].rson=tree;
    return tree+cur;
}

void Build(int root,int L,int R)
{
    Seg[root]=tree;
    if (L==R) return;

    int mid=(L+R)>>1;
    int Left=root<<1;
    int Right=Left|1;

    Build(Left,L,mid);
    Build(Right,mid+1,R);
}

int Query(Tnode *root,int L,int R,int x,int y)
{
    if ( y<L || R<x || root==tree ) return 0;//root==tree的时候退出,这是个很有力的优化!!
    if ( x<=L && R<=y ) return root->sum;

    int mid=(L+R)>>1;
    int vl=Query(root->lson,L,mid,x,y);
    int vr=Query(root->rson,mid+1,R,x,y);
    return (vl+vr);
}

int Ask(int root,int L,int R,int x,int y,int v)
{
    if ( y<L || R<x ) return 0;
    if ( x<=L && R<=y ) return Query(Seg[root],1,temp,1,v);

    int mid=(L+R)>>1;
    int Left=root<<1;
    int Right=Left|1;

    int vl=Ask(Left,L,mid,x,y,v);
    int vr=Ask(Right,mid+1,R,x,y,v);
    return (vl+vr);
}

void Push(int root,int L,int R,int x,int y)
{
    if ( y<L || R<x ) return;
    if ( x<=L && R<=y )
    {
        Root[++num]=Seg[root];
        return;
    }

    int mid=(L+R)>>1;
    int Left=root<<1;
    int Right=Left|1;

    Push(Left,L,mid,x,y);
    Push(Right,mid+1,R,x,y);
}

int Find(int L,int R,int Rank)
{
    if (L==R) return L;
    int Left=0,mid=(L+R)>>1;
    for (int i=1; i<=num; i++) Left+=Root[i]->lson->sum;
    if (Rank<=Left)
    {
        for (int i=1; i<=num; i++) Root[i]=Root[i]->lson;
        return Find(L,mid,Rank);
    }
    else
    {
        Rank-=Left;
        for (int i=1; i<=num; i++) Root[i]=Root[i]->rson;
        return Find(mid+1,R,Rank);
    }
}

void Update(Tnode *&root,int L,int R,int x,int v)
{
    if (root==tree) root=New_node();
    if (L==R)
    {
        root->sum+=v;
        return;
    }

    int mid=(L+R)>>1;
    if (x<=mid) Update(root->lson,L,mid,x,v);
    else Update(root->rson,mid+1,R,x,v);

    root->sum=root->lson->sum+root->rson->sum;
}

void Insert(int root,int L,int R,int x,int v)
{
    Update(Seg[root],1,temp,v,1);
    if (L==R) return;

    int mid=(L+R)>>1;
    int Left=root<<1;
    int Right=Left|1;

    if (x<=mid) Insert(Left,L,mid,x,v);
    else Insert(Right,mid+1,R,x,v);
}

void Delete(int root,int L,int R,int x,int v)
{
    Update(Seg[root],1,temp,v,-1);
    if (L==R) return;

    int mid=(L+R)>>1;
    int Left=root<<1;
    int Right=Left|1;

    if (x<=mid) Delete(Left,L,mid,x,v);
    else Delete(Right,mid+1,R,x,v);
}

int main()
{
    freopen("3380.in","r",stdin);
    freopen("3380.out","w",stdout);

    scanf("%d%d",&n,&m);
    for (int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
        work[i].val=a[i];
        work[i].l=i;
        work[i].opt=3;
    }
    int x=n;
    for (int i=1; i<=m; i++)
    {
        x++;
        scanf("%d",&work[x].opt);
        if (work[x].opt!=3)
        {
            scanf("%d%d",&work[x].l,&work[x].r);
            if (work[x].opt==2) scanf("%d",&work[x].k);
            else scanf("%d",&work[x].val);
        }
        else
        {
            work[x].opt=6;
            scanf("%d",&work[x].l);
            work[x].val=a[ work[x].l ];
            x++;
            work[x].opt=3;
            scanf("%d",&work[x].val);
            work[x].l=work[x-1].l;
            a[ work[x].l ]=work[x].val;
        }
    }

    for (int i=1; i<=x; i++) work[i].Time=i;
    sort(work+1,work+x+1,Comp1);
    temp=work[1].id=1;
    for (int i=2; i<=x; i++)
        if (work[i-1].val==work[i].val) work[i].id=temp;
        else work[i].id=++temp,b[temp]=work[i].val;
    sort(work+1,work+x+1,Comp2);

    New_node();
    Build(1,1,n);

    for (int i=1; i<=x; i++)
    {
        if (work[i].opt==1)
        {
            int ans=Ask(1,1,n,work[i].l,work[i].r,work[i].id-1)+1;
            printf("%d\n",ans);
        }
        if (work[i].opt==2)
        {
            num=0;
            Push(1,1,n,work[i].l,work[i].r);
            int ans=Find(1,temp,work[i].k);
            printf("%d\n",b[ans]);
        }
        if (work[i].opt==3) Insert(1,1,n,work[i].l,work[i].id);
        if (work[i].opt==4)
        {
            int ans;
            int Rank=Ask(1,1,n,work[i].l,work[i].r,work[i].id-1);
            if (!Rank) ans=-oo;
            else
            {
                num=0;
                Push(1,1,n,work[i].l,work[i].r);
                ans=b[ Find(1,temp,Rank) ];
            }
            printf("%d\n",ans);
        }
        if (work[i].opt==5)
        {
            int ans;
            int Rank=Ask(1,1,n,work[i].l,work[i].r,work[i].id);
            if (Rank==work[i].r-work[i].l+1) ans=oo;
            else
            {
                Rank++;
                num=0;
                Push(1,1,n,work[i].l,work[i].r);
                ans=b[ Find(1,temp,Rank) ];
            }
            printf("%d\n",ans);
        }
        if (work[i].opt==6) Delete(1,1,n,work[i].l,work[i].id);
    }

    //printf("%d\n",sizeof(tree)>>20);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值