洛谷P3380 【模板】二逼平衡树(树套树,树状数组,线段树)

洛谷题目传送门

emm。。。题目名写了个平衡树,但是这道题的理论复杂度最优解应该还是树状数组套值域线段树吧。

就像dynamic ranking那样(蒟蒻的Sol,放一个link骗访问量233)

所有的值(包括初始a数组,操作1、3、4、5的k)全部先丢进去离散化

对于1操作查比它小的数,挑log棵线段树,找区间小于这个数的个数+1,这个还比较好像

操作2就是dynamic ranking,log棵线段树一起加减,像静态主席树求第k小一样跳,操作3 dynamic ranking里也有

操作4先求小于这个数的个数,那么前驱的排名就等于这个个数,注意特判0就好了。

操作5也是先求排名再去找这个数,排名是小于且等于这个数的个数(等于查小于这个数在值域里加+1的数排名)

拼命卡常(小技巧,发现当前跳到的点size已经为0了就不用再往下跳了),然而常数还是丑。。。说不定应该破掉非递归版跑的比递归版多多少少快一点的谣言了?

#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define R RG int
#define II inline int
#define IV inline void
#define gc          if(++pi==iend)fread(pi=ibuf,1,SZ,stdin)
#define pc(C) *po=C;if(++po==oend)fwrite(po=obuf,1,SZ,stdout)
#define Q b+1,b+L+1
#define lb(X) X=lower_bound(Q,X)-b
using namespace std;
const int SZ=1<<20,N=50009,M=5000009,INF=2147483647;//卡了空间,M没到Nlog^2
char ibuf[SZ],obuf[SZ],*pi=ibuf+SZ-1,*po=obuf;
const char*iend=ibuf+SZ,*oend=obuf+SZ;
IV in(R&x){
    gc;
    while(*pi<‘-‘)gc;
    x=*pi&15;gc;
    while(*pi>‘-‘){x*=10;x+=*pi&15;gc;}
}
IV out(R x){
    if(x>9)out(x/10);
    pc(x%10|‘0‘);
}
int n,L,P,a[N],b[N<<1],op[N],ql[N],qr[N],qk[N];
int rt[N],lc[M],rc[M],s[M],ra[20],rs[20];
IV upd(R p,R k,R v){//更新
    for(R u,l,r,m,i=p;i<=n;i+=i&-i){
        if(!rt[i])rt[i]=++P;u=rt[i];l=1;r=L;
        while(l^r){
            s[u]+=v;m=(l+r)>>1;
            if(k<=m){r=m;if(!lc[u])lc[u]=++P;u=lc[u];}
            else  {l=m+1;if(!rc[u])rc[u]=++P;u=rc[u];}
        }
        s[u]+=v;
    }
}
II kth(R p,R k){//求第k小的值
    R i,l=1,r=L,m,sum,pa=0,ps=0;
    for(i=qr[p]  ;i;i-=i&-i)ra[++pa]=rt[i];
    for(i=ql[p]-1;i;i-=i&-i)rs[++ps]=rt[i];
    while(l^r){
        sum=0;m=(l+r)>>1;
        for(i=1;i<=pa;++i)sum+=s[lc[ra[i]]];
        for(i=1;i<=ps;++i)sum-=s[lc[rs[i]]];
        if(k<=sum){
            r=m;
            for(i=1,p=pa,pa=0;i<=p;++i)ra[++pa]=lc[ra[i]];
            for(i=1,p=ps,ps=0;i<=p;++i)rs[++ps]=lc[rs[i]];
        }
        else{
            l=m+1;k-=sum;
            for(i=1,p=pa,pa=0;i<=p;++i)ra[++pa]=rc[ra[i]];
            for(i=1,p=ps,ps=0;i<=p;++i)rs[++ps]=rc[rs[i]];
        }
    }
    return b[l];
}
II rank(R p,R x){//求离散化后的值的排名(从0计,也就是小于等于值的数的个数)
    R i,u,l,r,m,k=0;
    for(i=qr[p];i;i-=i&-i){
        u=rt[i];l=1;r=L;
        while(s[u]&&l^r){
            m=(l+r)>>1;
            if(x<=m)           r=m,u=lc[u];
            else k+=s[lc[u]],l=m+1,u=rc[u];
        }
    }
    for(i=ql[p]-1;i;i-=i&-i){
        u=rt[i];l=1;r=L;
        while(s[u]&&l^r){
            m=(l+r)>>1;
            if(x<=m)           r=m,u=lc[u];
            else k-=s[lc[u]],l=m+1,u=rc[u];
        }
    }
    return k;
}
int main(){
    R m,i,rk;
    in(n);in(m);L=n;
    for(i=1;i<=n;++i)in(a[i]);
    memcpy(b,a,(n+1)<<2);
    for(i=1;i<=m;++i){
        in(op[i]);//先存起来
        in(ql[i]);if(op[i]!=3)in(qr[i]);
        in(qk[i]);if(op[i]!=2)b[++L]=qk[i];
    }
    b[++L]=INF;sort(Q);L=unique(Q)-b-1;
    for(i=1;i<=n;++i)
        upd(i,lb(a[i]),1);
    for(i=1;i<=m;++i){
        if(op[i]!=2)lb(qk[i]);
        switch(op[i]){
            case 1:out(rank(i,qk[i])+1);pc(‘\n‘);break;
            case 2:out(kth(i,qk[i]));pc(‘\n‘);break;
            case 3:upd(ql[i],a[ql[i]],-1);
                upd(ql[i],a[ql[i]]=qk[i],1);break;
            case 4:rk=rank(i,qk[i]);
                if(rk)out(kth(i,rk));
                else{pc(‘-‘);out(INF);}pc(‘\n‘);break;
            case 5:rk=rank(i,qk[i]+1);
                out(rk>qr[i]-ql[i]?INF:kth(i,rk+1));pc(‘\n‘);
        }
    }
    fwrite(obuf,1,po-obuf,stdout);
    return 0;
}

原文地址:https://www.cnblogs.com/flashhu/p/8783459.html

时间: 2024-08-15 17:23:13

洛谷P3380 【模板】二逼平衡树(树套树,树状数组,线段树)的相关文章

hdu 1166 树状数组 线段树

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 51177    Accepted Submission(s): 21427 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务

hdu1394(枚举/树状数组/线段树单点更新&amp;区间求和)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1394 题意:给出一个循环数组,求其逆序对最少为多少: 思路:对于逆序对: 交换两个相邻数,逆序数 +1 或 -1, 交换两个不相邻数 a, b, 逆序数 += 两者间大于 a 的个数 - 两者间小于 a 的个数: 所以只要求出初始时的逆序对数,就可以推出其余情况时的逆序对数.对于求初始逆序对数,这里 n 只有 5e3,可以直接暴力 / 树状数组 / 线段树 / 归并排序: 代码: 1.直接暴力 1

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

HDU 1166 敌兵布阵 (树状数组&#183;线段树)

题意  中文 动态区间和问题   只会更新点  最基础的树状数组 线段树的应用 树状数组代码 #include <bits/stdc++.h> using namespace std; const int N = 50005; int c[N], n, m; void add(int p, int x) { while(p <= n) c[p] += x, p += p & -p; } int getSum(int p) { int ret = 0; while(p > 0

[洛谷P1198/BZOJ1012][JSOI2008] 最大数 - 树状数组/线段树?

其实已经学了树状数组和线段树,然而懒得做题,所以至今没写多少博客 Description 现在请求你维护一个数列,要求提供以下两种操作: 1. 查询操作. 语法:Q L 功能:查询当前数列中末尾L个数中的最大的数,并输出这个数的值. 限制:L不超过当前数列的长度.(L>=0) 2. 插入操作. 语法:A n 功能:将n加上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾. 限制:n是整数(可能为负数)并且在长整

树状数组 线段树

树状数组 树状数组的基本用途是维护序列的前缀和,相比前缀和数组,树状数组优势在于高效率的单点修改,单点增加(前缀和数组单点修改效率比较低) 因为树状数组的思想,原理还是很好理解的,就直接讲基本算法; 1 lowbit函数 关于lowbit这个函数,可能会有点难以理解,但其实你不理解也没关系,把模板背下来就好 根据任意正整数关于2的不重复次幂的唯一分解性质,例如十进制21用二进制表示为10101,其中等于1的位是第0,2,4(最右端是第0位)位,即21被二进制分解成\(2^4+2^2+2^0\);

【bzoj4785】[Zjoi2017]树状数组 线段树套线段树

题目描述 漆黑的晚上,九条可怜躺在床上辗转反侧.难以入眠的她想起了若干年前她的一次悲惨的OI 比赛经历.那是一道基础的树状数组题.给出一个长度为 n 的数组 A,初始值都为 0,接下来进行 m 次操作,操作有两种: 1 x,表示将 Ax 变成 (Ax + 1) mod 2. 2 l r,表示询问 sigma(Ai) mod 2,L<=i<=r 尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做.当时非常young 的她写了如下的算法: 1: function Add(x

hdu 1166 敌兵布阵——(区间和)树状数组/线段树

here:http://acm.hdu.edu.cn/showproblem.php?pid=1166 Input 第一行一个整数T.表示有T组数据. 每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地.接下来有N个正整数,第i个正整数ai代表第i个工兵营地里開始时有ai个人(1<=ai<=50). 接下来每行有一条命令.命令有4种形式: (1) Add i j,i和j为正整数,表示第i个营地添加j个人(j不超过30) (2)Sub i j ,i和j为正整数,表示第i

BZOJ_1901_&amp;_ZJU_2112_Dynamic_Rankings(主席树+树状数组/线段树+(Treap/Splay))

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1901 给出一个长度为n的数列A,有m次询问,询问分两种:1.修改某一位置的值;2.求区间[l,r]内的第k小的值. 分析 对于只有第一种询问的问题: POJ_2104_Kth(主席树) 现在要求动态.我们思考这样一个问题:把求区间第k小的问题变成求区间和值的问题,这个好解决吧?对于静态的问题,我们使用前缀和即可解决,那么对于动态的呢?使用树状数组维护前缀和.那么现在把问题变回求区间第k小值的

CCPC河南省赛B-树上逆序对| 离线处理|树状数组 + 线段树维护逆序对 + dfs序 + 离散化

B题地址:树上逆序对 有两个思路 方法一:线段树离线 + 树状数组或者线段树维护区间和 0:离散化,离线存储输入的operation操作序列. ①:先线段树在dfs序上离线处理好整一棵树:在dfs序上先查询"加入当前结点的逆序对权值和"并记录,再加入当前这个节点:dfs完毕后,就已经记录好每个结点的dfs序出入时间戳(转化成区间问题了)和每个 ②:使用树状数组或者新的线段树在dfs序上插入逆序对权值 为什么能这样呢?因为dfs序维护了每个结点遍历的顺序,每个结点的dfs序时间戳肯定比它