bzoj 1901/zoj 2112(主席树+树状数组)

传送门
题解:又一道《万年A不掉》系列。外层树状数组维护内层权值线段树,修改/查询/空间复杂度都是(O(nlog(n)^2))。关于前缀,由静态root[r]-root[l]变为两个按树状数组lowbit处理过的root池(pl,pr)相减。终于大概明白树状数组到底怎么在外层维护权值线段树orz,树状数组大法好(=゚ω゚)ノ.

P.S.如果此时n变大(zoj 2112),就需要优化空间。对于静态的建树只要nlogn个节点就可以了,而且对于修改操作,只是修改M次,每次改变俩个值(减去原先的,加上现在的)。也就是说如果把所有初值都插入到树状数组里会大幅浪费空间,所以初值按照静态来建,内存O(nlogn),而修改部分保存在树状数组中,每次修改logn棵树,每次插入增加logn个节点O(mlognlogn+nlogn)。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN=1e4+4;
int n,m,a[MAXN],b[MAXN<<1];//b[]需要两倍!!! 
int tot=0,cnt=0,nn,root[MAXN],lc[MAXN*226],rc[MAXN*226],siz[MAXN*226];//MAXN*log(MAXN)^2
int cl,cr,pl[MAXN],pr[MAXN];//按lowbit存储query用到的root集合
struct Q {
    int l,r,k;
}q[MAXN];
inline int read() {
    int x=0;char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x;
}
void modify(int &rt,int l,int r,int val,int delta) {
    lc[++tot]=lc[rt],rc[tot]=rc[rt],siz[tot]=siz[rt];
    rt=tot,siz[rt]+=delta;
    if (l==r) return ;
    int mid=(l+r)>>1;
    if (val<=mid) modify(lc[rt],l,mid,val,delta);
    else modify(rc[rt],mid+1,r,val,delta);
}
inline void add(int x,int val,int delta) {
    for (;x<=n;x+=x&(-x)) modify(root[x],1,nn,val,delta);
}
int query(int l,int r,int k) {
    if (l==r) return l;
    int suml=0,sumr=0;
    for (int i=1;i<=cl;++i) suml+=siz[lc[pl[i]]];
    for (int i=1;i<=cr;++i) sumr+=siz[lc[pr[i]]];
    int mid=(l+r)>>1;
    if (k<=sumr-suml) {
        for (int i=1;i<=cl;++i) pl[i]=lc[pl[i]];
        for (int i=1;i<=cr;++i) pr[i]=lc[pr[i]];
        return query(l,mid,k);
    }
    else {
        for (int i=1;i<=cl;++i) pl[i]=rc[pl[i]];
        for (int i=1;i<=cr;++i) pr[i]=rc[pr[i]];
        return query(mid+1,r,k-sumr+suml);
    }
}
inline int ask(int l,int r,int k) {
    for (cl=0;l;l-=l&(-l)) pl[++cl]=root[l];//记录"左前缀"
    for (cr=0;r;r-=r&(-r)) pr[++cr]=root[r];//记录"右前缀"
    return query(1,nn,k);
}
int main() {
//  freopen("bzoj 1901.in","r",stdin);
    n=read(),m=read();
    for (register int i=1;i<=n;++i) b[++cnt]=a[i]=read();
    for (register int i=1;i<=m;++i) {
        char s;while (!isalpha(s=getchar()));
        if (s^'Q') q[i].l=read(),b[++cnt]=q[i].r=read(),q[i].k=0;
        else q[i].l=read(),q[i].r=read(),q[i].k=read();
    }
    sort(b+1,b+cnt+1);
    nn=unique(b+1,b+cnt+1)-b-1;//离线离散化
    for (register int i=1;i<=n;++i) add(i,a[i]=lower_bound(b+1,b+nn+1,a[i])-b,1);
    for (register int i=1;i<=m;++i) {
        if (q[i].k) printf("%d\n",b[ask(q[i].l-1,q[i].r,q[i].k)]);
        else {
            add(q[i].l,a[q[i].l],-1);
            a[q[i].l]=lower_bound(b+1,b+nn+1,q[i].r)-b;
            add(q[i].l,a[q[i].l],1);
        }
    }
    return 0;
}
发布了344 篇原创文章 · 获赞 14 · 访问量 5万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览