动态主席树 优化版

和大奕哥一起学习了主席树,机房里杰哥看了之后一直说可以优化空间,题解写的太low了,所以我今天写了一个动态开点的线段树来优化一波空间,bz上试了一下,确实比网上看的题解省空间。

网上的题解是每一次都新开点,是可持久化的写法,我写的是正经的线段树动态开点。

附上代码

#include<bits/stdc++.h>
using namespace std;
const int N=10010,INF=1e9+10;
int n,m,pl,tl,mx;
int a[N<<1],num[N<<1],crt[N<<1];
char s[10];
struct tnode
{
    int lc,rc,cnt;
}t[N*120];
struct ques
{
    int l,r,k,x,d;
    bool tmp;
}q[N];
struct node
{
    int d,id;
}p[N<<1];
bool cmp(node x,node y) {return x.d<y.d;}
void merge(int x)
{
    t[x].cnt=t[t[x].lc].cnt+t[t[x].rc].cnt;
}
void update(int &rt,int p,int d,int l,int r)
{
    if(!rt) rt=++tl;
    if(l==r)
    {
        t[rt].cnt+=d;
        return;
    }
    int mid=l+r>>1;
    if(p<=mid) update(t[rt].lc,p,d,l,mid);
    else update(t[rt].rc,p,d,mid+1,r);
    merge(rt);
}
void add(int x,int p,int d)
{
    for(int i=x;i<=n;i+=i&-i) update(i,p,d,1,mx);
}
int getsum(int x)
{
    int ans=0;
    for(int i=x;i;i-=i&-i) ans+=t[t[crt[i]].lc].cnt;
    return ans;
}
int query(int lx,int rx,int k)
{
    for(int i=lx-1;i>=1;i-=(i&(-i))) crt[i]=i;
    for(int i=rx;i>=1;i-=(i&(-i))) crt[i]=i;
    int l=1,r=mx,mid,sum;
    while(l<r)
    {
        mid=(l+r)/2;
        sum=getsum(rx)-getsum(lx-1);
        if(sum>=k)
        {
            r=mid;
            for(int i=lx-1;i>=1;i-=(i&(-i))) crt[i]=t[crt[i]].lc;
            for(int i=rx;i>=1;i-=(i&(-i))) crt[i]=t[crt[i]].lc;
        }
        else
        {
            l=mid+1;
            k-=sum;
            for(int i=lx-1;i>=1;i-=(i&(-i))) crt[i]=t[crt[i]].rc;
            for(int i=rx;i>=1;i-=(i&(-i))) crt[i]=t[crt[i]].rc;
        }
    }
    return l;
}
int main()
{
    scanf("%d%d",&n,&m);
    pl=n;tl=n;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        p[i].d=a[i];p[i].id=i;
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%s",s);
        if(s[0]==‘Q‘)
        {
            q[i].tmp=0;
            scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);
        }
        else
        {
            q[i].tmp=1;
            scanf("%d%d",&q[i].x,&q[i].d);
            p[++pl].d=q[i].d;p[pl].id=n+i;
        }
    }
    sort(p+1,p+1+pl,cmp);
    mx=0;p[0].d=INF;
    for(int i=1;i<=pl;i++)
    {
        if(p[i].d!=p[i-1].d) mx++,num[mx]=p[i].d;
        if(p[i].id<=n) a[p[i].id]=mx;
        else q[p[i].id-n].d=mx;
    }
    for(int i=1;i<=n;i++)
        add(i,a[i],1);
    for(int i=1;i<=m;i++)
    {
        if(q[i].tmp==0)
            printf("%d\n",num[query(q[i].l,q[i].r,q[i].k)]);
        else
        {
            add(q[i].x,a[q[i].x],-1);
            add(q[i].x,q[i].d,1);
            a[q[i].x]=q[i].d;
        }
    }
    return 0;
}
时间: 2024-11-03 20:08:07

动态主席树 优化版的相关文章

BZOJ 3218 A+B Problem(最大流 + 主席树优化建图)

题目:A+B Problem 感谢 Nietzsche 在省选紧迫之际花 39' 给我讲这道题. 这题我并没有想出来,感觉又浪费一道好题了. 需要用最小割,建模方式如下(假设若 2 取黑色,1 取白色会使 2 为奇怪方格): 跑一边最大流,求出最小割,用所有的 W + 所有的 B - 最小割,就是答案. 不过,对于每一个结点 2,在寻找像 1 这样(li <= aj <= ri)的结点时,总不能一个一个枚举吧? O(n2) T 飞. 所以,需要用主席树优化一下.线段树优化建图笔记. 代码未完待

zoj 2112 Dynamic Rankings 带修改区间第K大 动态主席树

pass 首先,个人觉得把这个数据结构理解成树状数组套主席树是十分不严谨的.主席树的本质是可持久化权值线段树与前缀和思想的结合.而动态主席树是可持久化权值线段树与树状数组思想的结合.并非树套树般的泾渭分明的叠加. 其次,大概讲下对动态主席树的理解.我们静态主席树中,第i个版本维护的是[1,i]的权值线段树,我们利用前缀和的思想,通过y的版本-x的版本得到[x,y]的权值线段树,从而剥离出一颗对应区间的权值线段树.我们考虑在这个情况下,如果需要修改第a[i]的值,那么从i,i+1,i+2.....

【模板】动态主席树

如题,这是一个模板... 1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 #include <cctype> 6 7 inline void read(int & x) 8 { 9 x = 0; 10 int k = 1; 11 char c = getchar(); 12 while (!isdigit(c))

ZOJ 2112 Dynamic Rankings(主席树套树状数组+静态主席树)

题意:给定一个区间,求这个区间第k大的数,支持单点修改. 思路:主席树真是个神奇的东西.........速度很快但是也有一个问题就是占用内存的很大,一般来说支持单点修改的主席树套树状数组空间复杂度为O(n*logn*logn), 如果查询较少的话,可以初始的时候用一颗静态主席树,这样空间复杂度可以降为O(n*logn+q*logn*logn),勉强可以过zoj这道题. 这道题看了好久好久才懂...网上题解一般就几句话.......下面是自己的一些理解. 首先静态主席树这个东西其实比较好懂,就是对

bzoj2588 -- 树链剖分+主席树

先将权值离散. 显然可以对于每个结点建一棵权值线段树存这个点到根结点的路径上的点权,询问时在线段树上二分,但这样时间是O(n2log2n)的. 然后想到用主席树优化,时间复杂度O(n*log2n). 代码: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<vector> 6 using namespace st

【算法】主席树

这是一篇有关主席树的总结 主席树是什么? 对于原序列的每一个前缀[1···i]建立出一棵线段树维护值域上每个数出现的次数,则其树是可减的 PS:本篇随笔对于主席树的基本内容并没有深刻讲解,主要说明它的一些用法 其实就是很多一堆大量的权值线段树 (什么是权值线段树?就是每个节点维护不是位置,而是权值,比如 \([1,4]\) 维护的就是权值等于1到权值等于4的信息) 而且这些线段树还隐含了一个前缀和的功能 最简单的就是一个数列,长度为 \(n\) ,那么就建 \(n\) 棵权值线段树,第 \(i\

主席树(一种可持久化线段树)

study from: 静态主席树:https://blog.csdn.net/a1351937368/article/details/78884526 动态主席树:https://blog.csdn.net/WilliamSun0122/article/details/77885781 静态: https://www.luogu.org/problemnew/show/P3834 1 #include <cstdio> 2 #include <cstdlib> 3 #includ

我想要打一个主席树

这里提供了静态主席树及动态主席树(树状数组套主席树)的模板. 分别应用于静态区间第k小及动态区间第k小. 静态: #include<cstdio> #include<iostream> #include<algorithm> #include<cmath> #include<cstring> #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC target("avx&q

[CQOI2011]动态逆序对(主席树,树状数组)

[CQOI2011]动态逆序对(luogu) 题目描述 对于序列 aa,它的逆序对数定义为集合 \{(i,j)| i<j \wedge a_i > a_j \}{(i,j)∣i<j∧ai?>aj?} 中的元素个数. 现在给出 1\sim n1∼n 的一个排列,按照某种顺序依次删除 mm 个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数. 输入格式 第一行包含两个整数 nn 和 mm,即初始元素的个数和删除的元素个数.以下 nn 行,每行包含一个 1 \sim n1∼n