ZOJ 2112(树套树)

题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemId=1112

 

静态的主席树用于记录静态的未修改的序列的贡献(板子)

对于单点修改来说,修改id处的数字相当于将该棵主席树以及在此基础上的主席树都修改掉,那么单次修改的复杂度就会达到n*log(n),而这个操作就相当于区间修改吧,那么最适合做区间修改,单点查询的是什么

树状数组啊

在这里的这颗树状数组的每个节点都是一颗权值线段树,如果每次修改我们都新建一颗权值线段树,空间扛不住啊,于是我们可以将每次修改的部分新建,不受影响的部分直接划等号拉过来就可以。

显然这种操作需要将所有操作离线才可。

将id的值修改为x,就相当于我们先把权值线段树中原来id的值对应的位置减一,再把x对应的位置加一。

最开始的时候还没有进行修改,那么可以新建一颗权值全部为空的权值线段树,树状数组所有的点都连到这颗树上,之后进行修改在自身的基础上搞一搞就可以了。

这里的树状数组与主席树没有直接联系!可以说这是两块东西分别维护需要维护的东西。

这道题多推敲,一定要想一想树里面的东西到底是什么

参考博客:https://blog.csdn.net/u014664226/article/details/47839973

https://blog.csdn.net/creatorx/article/details/75581617

#include<bits/stdc++.h>
using namespace std;

const int maxn=5e4+7;
const int maxm=1e4+7;

int m;
int tot;

int a[maxn],b[maxn+maxm];

struct Tree{
    int sum;
    int lc,rc;
}tree[2136839];

int root[maxn];

void out(){
    cout<<(int)(maxn*4+maxn*log2(maxn)+maxm*log2(maxn))<<endl;
}

void init(int n){
    tot=0;
    sort(b+1,b+1+n);
    m=unique(b+1,b+1+n)-b-1;
}

int getid(int x){
    return lower_bound(b+1,b+1+m,x)-b;
}

int build(int l,int r){
    int k=++tot;
    tree[k].sum=0;
    if(l==r) return k;
    int mid=(l+r)>>1;
    tree[k].lc=build(l,mid);
    tree[k].rc=build(mid+1,r);
    return k;
}

int updata(int p,int l,int r,int id,int val){
    int k=++tot;
    tree[k]=tree[p];
    if(l==r){
        tree[k].sum+=val;
        return k;
    }
    int mid=(l+r)>>1;
    if(id<=mid) tree[k].lc=updata(tree[p].lc,l,mid,id,val);
    else tree[k].rc=updata(tree[p].rc,mid+1,r,id,val);
    tree[k].sum=tree[tree[k].lc].sum+tree[tree[k].rc].sum;
    return k;
}

int S[maxn];//树状数组中线段树的最开始的节点;
int X[maxn],Y[maxn];//树状数组中该次查找会用到的线段树的节点编号;
int n;

int lowbit(int x){
    return x&(-x);
}

void add(int x,int id,int val){
    for(;x<=n;x+=lowbit(x))
        S[x]=updata(S[x],1,m,id,val);
}

int getsum(int x,int a[]){
    int res=0;
    for(;x;x-=lowbit(x))
        res+=tree[tree[a[x]].lc].sum;
    return res;
}

//区间查询非递归写法;
int myfind(int p,int q,int k){
    int l=1,r=m,mid;
    int left_root=root[q-1],right_root=root[p];
    for(int x=p;x;x-=lowbit(x)) Y[x]=S[x];
    for(int x=q-1;x;x-=lowbit(x)) X[x]=S[x];
    while(l<r){
        mid=(l+r)>>1;
        int summ=getsum(p,Y)-getsum(q-1,X)+tree[tree[right_root].lc].sum-tree[tree[left_root].lc].sum;
        if(k<=summ){
            r=mid;
            for(int x=p;x;x-=lowbit(x)) Y[x]=tree[Y[x]].lc;
            for(int x=q-1;x;x-=lowbit(x)) X[x]=tree[X[x]].lc;
            left_root=tree[left_root].lc;
            right_root=tree[right_root].lc;
        }
        else{
            k-=summ;
            l=mid+1;
            for(int x=p;x;x-=lowbit(x)) Y[x]=tree[Y[x]].rc;
            for(int x=q-1;x;x-=lowbit(x)) X[x]=tree[X[x]].rc;
            left_root=tree[left_root].rc;
            right_root=tree[right_root].rc;
        }

    }
    return l;
}

struct Node{
    int l,r;
    int k;
    bool f;
}qq[maxm];
char s[9];

int main(){
    //out();
    int t,q;
    scanf("%d",&t);
    while(t--){
        int len=0;
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;++i){
            scanf("%d",&a[i]);
            b[++len]=a[i];
        }
        for(int i=1;i<=q;++i){
            scanf("%s",s);
            if(s[0]=='Q'){
                scanf("%d%d%d",&qq[i].l,&qq[i].r,&qq[i].k);
                qq[i].f=1;
            }
            else{
                scanf("%d%d",&qq[i].l,&qq[i].r);
                qq[i].f=0;
                b[++len]=qq[i].r;
            }
        }

        init(len);
        //cout<<m<<endl;
        root[0]=build(1,m);
        for(int i=1;i<=n;++i) root[i]=updata(root[i-1],1,m,getid(a[i]),1);
        for(int i=1;i<=n;++i) S[i]=root[0];
        for(int i=1;i<=q;++i){
            if(qq[i].f) printf("%d\n",b[myfind(qq[i].r,qq[i].l,qq[i].k)]);
            else{
                add(qq[i].l,getid(a[qq[i].l]),-1);
                add(qq[i].l,getid(qq[i].r),1);
                a[qq[i].l]=qq[i].r;

            }
        }

    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值