可修改区间k-th number

来源:互联网 发布:如何安装ubuntu虚拟机 编辑:程序博客网 时间:2024/06/06 09:00

不带修改的区间k-th number

这个众所周知了。可以用主席树实现。
具体的话其实就是开n棵权值线段树,动态开节点,由于每次增加一个点最多只会新建log2n个点,所以空间复杂度为O(nlog2n)
我们在权值线段树上记录该范围数的出现次数,然后可以发现查询的两个区间端点的线段树信息可以相减,于是就可以线段树上二分了。时间复杂度O(nlog2n)

可修改区间k-th number

%了一波Samjia2000
注意到像上面那样做的话,修改一个点就要把它后面所有点也一起修改,显然TLE。
上面的方法是不能兹瓷修改的。
那我们就需要树套树!

修改操作

我们用一个BIT,BIT上的每个点都是一棵线段树,位置为x的线段树记录了其前面lowbit(x)个以BIT的结构过来的点的信息。这是一种变形的前缀和。
(请注意,主席树是真实的前缀和,而用树状数组则是伪前缀和)
修改操作,就在BIT上往后跳,跳到一个点就改一个点。
修改操作,就是BIT套线段树。
单次修改时间复杂度为O(log22n)

查询操作

有了记录前缀信息的BIT,就能像BIT一样,往前跳。
起初我以为要把所有这些线段树合并到答案里,但这样做麻烦,而且时间很慢。
其实可以先记录好所需的线段树的编号,然后直接进入线段树,在线段树里再把记录的编号拿出来,两个端点相减二分。当在线段树里二分时,所记录的编号里面的所有点也要相应地走到左儿子或右儿子。
查询操作,就是线段树套BIT。
单次查询时间复杂度为O(log22n)

总结

总的时间复杂度为O((n+m)log22n)
空间复杂度为O((n+m)log22n)

Code

#include<cstdio>#include<algorithm>#define fo(i,a,b) for(int i=a;i<=b;i++)using namespace std;const int N=10010,M=2000010;int n,m,num,a[N],lv[N],rv[N],root[N],ls[M],rs[M],sum[M];int lowbit(int x){return x&-x;}void modify(int &v,int l,int r,int x,int p)//single point modify{    if(!v) v=++num;    sum[v]+=p;    if(l==r) return;    int mid=(l+r)>>1;    if(x<=mid) modify(ls[v],l,mid,x,p);    else modify(rs[v],mid+1,r,x,p);}void add(int x,int y,int p){    for(;x<=n;x+=lowbit(x)) modify(root[x],1,n,y,p);}void turnL(){    fo(i,1,rv[0]) rv[i]=ls[rv[i]];    fo(i,1,lv[0]) lv[i]=ls[lv[i]];}void turnR(){    fo(i,1,rv[0]) rv[i]=rs[rv[i]];    fo(i,1,lv[0]) lv[i]=rs[lv[i]];}int query(int l,int r,int k)//k-th number{    if(l==r) return l;    int mid=(l+r)>>1,t=0;    fo(i,1,rv[0]) t+=sum[rs[rv[i]]];    fo(i,1,lv[0]) t-=sum[rs[lv[i]]];    if(t>=k)    {        turnR();        return query(mid+1,r,k);    }    else    {        turnL();        return query(l,mid,k-t);    }}int main(){    int tp,_,x,y,k;    scanf("%d",&n);    fo(i,1,n)    {        scanf("%d",&a[i]);        add(i,a[i],1);    }    scanf("%d",&_);    while(_--)    {        scanf("%d %d %d",&tp,&x,&y);        if(tp==0)        {            add(x,a[x],-1);            a[x]=y;            add(x,a[x],1);        }        else        {            scanf("%d",&k);            lv[0]=rv[0]=0;            for(int i=y;i;i-=lowbit(i)) rv[++rv[0]]=root[i];            for(int i=x-1;i;i-=lowbit(i)) lv[++lv[0]]=root[i];            //record position in BIT            printf("%d\n",query(1,n,k));        }    }    return 0;}
1 0