【BZOJ】1901 Zju2112 Dynamic Rankings 树状数组+主席树

80 篇文章 1 订阅
13 篇文章 0 订阅

题目传送门

树状数组套主席树什么的真的好迷啊……还是整体二分比较平易近人(大雾)。

我们考虑主席树的修改,如果像以前一样前缀动态开点,那么修改一个点就要把后面的主席树全部重建,时间复杂度 O(m×n×logn) ,和暴力差不多嘛……

然后我们考虑在主席树外面套一个树状数组,这样既不会破坏前缀动态开点的性质,同时把时间复杂度降到了 O(m×log2n) ,岂不是美哉?

那么现在的每一个单独的主席树已经不能表示一个区间了,而是用几棵主席树来表示一个区间了,所以才有了那么麻烦的查询。

至于修改,就对于每一个树状数组的节点像主席树一样修改就好了。

然后这题就被A掉啦。

p.s.这是我第一次看到运行0ms的TLE……

orz sillyf

附上AC代码:

#include <cstdio>
#include <cctype>
#include <algorithm>
#define lowbit(x) ((x)&(-x))
using namespace std;

const int N=1e4+10;
struct tree{
    int lt,rt,sum;
}t[6000010];
int n,m,a[N],b[N<<1],num,q[N][4],tmp,root[N],size,len1,len2,L[30],R[20];

inline char nc(void){
    static char ch[100010],*p1=ch,*p2=ch;
    return p1==p2&&(p2=(p1=ch)+fread(ch,1,100010,stdin),p1==p2)?EOF:*p1++;
}

inline void read(int &a){
    static char c=nc();int f=1;
    for (;!isdigit(c);c=nc()) if (c=='-') f=-1;
    for (a=0;isdigit(c);a=(a<<3)+(a<<1)+c-'0',c=nc());
    return (void)(a*=f);
}

#define mid (l+r>>1)
inline void updata(int pre,int &k,int l,int r,int wz,int w){
    t[k=++size]=t[pre],t[k].sum+=w;
    if (l==r) return;
    if (mid>=wz) updata(t[pre].lt,t[k].lt,l,mid,wz,w);
    else updata(t[pre].rt,t[k].rt,mid+1,r,wz,w);
    return;
}

inline int query(int l,int r,int w){
    if (l==r) return l;
    int sum1=0,sum2=0;
    for (int i=1; i<=len1; ++i) sum1+=t[t[L[i]].lt].sum;
    for (int i=1; i<=len2; ++i) sum2+=t[t[R[i]].lt].sum;
    if (sum2-sum1>=w){
        for (int i=1; i<=len1; ++i) L[i]=t[L[i]].lt;
        for (int i=1; i<=len2; ++i) R[i]=t[R[i]].lt;
        query(l,mid,w);
    }
    else {
        for (int i=1; i<=len1; ++i) L[i]=t[L[i]].rt;
        for (int i=1; i<=len2; ++i) R[i]=t[R[i]].rt;
        query(mid+1,r,w-sum2+sum1);
    }
}

int main(void){
    read(n),read(m),num=n;
    for (int i=1; i<=n; ++i) read(a[i]),b[i]=a[i];
    for (int i=1; i<=m; ++i){
        char c=nc();while (c!='C'&&c!='Q') c=nc();
        read(q[i][1]),read(q[i][2]);
        if (c=='Q') read(q[i][3]); else b[++num]=q[i][2];
    }
    sort(b+1,b+1+num),num=unique(b+1,b+1+num)-b-1;
    for (int i=1; i<=n; ++i)
        for (int j=i; j<=n; j+=lowbit(j)){
            tmp=lower_bound(b+1,b+1+num,a[i])-b;
            updata(root[j],root[j],1,num,tmp,1);
        }
    for (int i=1; i<=m; ++i)
        if (q[i][3]){
            len1=len2=0;
            for (int j=q[i][1]-1; j; j-=lowbit(j)) L[++len1]=root[j];
            for (int j=q[i][2]; j; j-=lowbit(j)) R[++len2]=root[j];
            printf("%d\n",b[query(1,num,q[i][3])]);
        }
        else {
            tmp=lower_bound(b+1,b+1+num,a[q[i][1]])-b;
            for (int j=q[i][1]; j<=n; j+=lowbit(j)) updata(root[j],root[j],1,num,tmp,-1);
            a[q[i][1]]=q[i][2],tmp=lower_bound(b+1,b+1+num,q[i][2])-b;
            for (int j=q[i][1]; j<=n; j+=lowbit(j)) updata(root[j],root[j],1,num,tmp,1);
        }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值