• [学习笔记]带修改主席树


    1、Dynamic Rankings

    区间带修改的第 (k) 大需要用带修改主席树。

    如果用平常的主席树的效率是多少呢?

    查询 (O(logn)),暴力修改 (O(nlogn)),时间不支持

    那么就需要平衡一下两者的时间复杂度

    我们用树状数组套主席树,每次查询把 (logn)(rt) 取出来,(l-1)(r)(sum) 相减一下,在值域进行 (logn) 的遍历,时间复杂度 (O(nlog^2n))

    int query(int l,int r,int k){
    	if(l == r) return l;
    	int x=0,mid=(l+r)>>1;
    	for(int i=1;i<=cnt1;i++) x-=sum[L[lT[i]]];
    	for(int i=1;i<=cnt2;i++) x+=sum[L[rT[i]]];
    	if(x >= k){
    		for(int i=1;i<=cnt1;i++) lT[i]=L[lT[i]];
    		for(int i=1;i<=cnt2;i++) rT[i]=L[rT[i]];
    		return query(l,mid,k);
    	}
    	else {
    		for(int i=1;i<=cnt1;i++) lT[i]=R[lT[i]];
    		for(int i=1;i<=cnt2;i++) rT[i]=R[rT[i]];
    		return query(mid+1,r,k-x);
    	}
    }
    

    每次修改在树状数组上,遍历到一个点就 (update),动态开点

    void update(int &now,int l,int r,int x,int v){
    	if(!now) now=++tot;
    	sum[now]+=v;
    	if(l == r) return ;
    	int mid=(l+r)>>1;
    	if(x <= mid) update(L[now],l,mid,x,v);
    	else update(R[now],mid+1,r,x,v);
    }
    
    void add(int x,int v){
    	for(int i=x;i<=n;i+=lowbit(i))
    		update(T[i],1,n,a[x],v);
    }
    

    每次修改要新开一条链上 (logn) 个结点,一共执行 (logn) 次,空间复杂度 (O(nlog^2n))

    不难证明时间复杂度 (O(nlog^2n))

    (Code Below:)

    #include <bits/stdc++.h>
    #define lowbit(x) ((x)&(-(x)))
    using namespace std;
    const int maxn=100000+10;
    int n,m,a[maxn],mp[maxn<<1],cnt;
    int T[maxn],lT[maxn],rT[maxn],L[maxn*400],R[maxn*400],sum[maxn*400],tot,cnt1,cnt2;
    
    struct Query{
    	int opt,l,r,k;
    }q[maxn];
    
    inline int read(){
    	register int x=0,f=1;char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return (f==1)?x:-x;
    }
    
    void update(int &now,int l,int r,int x,int v){
    	if(!now) now=++tot;
    	sum[now]+=v;
    	if(l == r) return ;
    	int mid=(l+r)>>1;
    	if(x <= mid) update(L[now],l,mid,x,v);
    	else update(R[now],mid+1,r,x,v);
    }
    
    void add(int x,int v){
    	for(int i=x;i<=n;i+=lowbit(i)) 
    		update(T[i],1,cnt,a[x],v);
    }
    
    int query(int l,int r,int k){
    	if(l == r) return l;
    	int x=0,mid=(l+r)>>1;
    	for(int i=1;i<=cnt1;i++) x-=sum[L[lT[i]]];
    	for(int i=1;i<=cnt2;i++) x+=sum[L[rT[i]]];
    	if(x >= k){
    		for(int i=1;i<=cnt1;i++) lT[i]=L[lT[i]];
    		for(int i=1;i<=cnt2;i++) rT[i]=L[rT[i]];
    		return query(l,mid,k);
    	}
    	else {
    		for(int i=1;i<=cnt1;i++) lT[i]=R[lT[i]];
    		for(int i=1;i<=cnt2;i++) rT[i]=R[rT[i]];
    		return query(mid+1,r,k-x);
    	}
    }
    
    int main()
    {
    	n=read(),m=read();
    	for(int i=1;i<=n;i++) mp[++cnt]=a[i]=read();
    	char ch;
    	for(int i=1;i<=m;i++){
    		ch=getchar();
    		while(!isalpha(ch)) ch=getchar();
    		q[i].opt=(ch=='Q')?1:2;
    		if(q[i].opt==1) q[i].l=read(),q[i].r=read(),q[i].k=read();
    		else q[i].l=read(),mp[++cnt]=q[i].k=read();
    	}
    	sort(mp+1,mp+cnt+1);
    	cnt=unique(mp+1,mp+cnt+1)-mp-1;
    	for(int i=1;i<=n;i++) a[i]=lower_bound(mp+1,mp+cnt+1,a[i])-mp,add(i,1);
    	for(int i=1;i<=m;i++){
    		if(q[i].opt==1){
    			cnt1=cnt2=0;
    			for(int j=q[i].l-1;j>0;j-=lowbit(j)) lT[++cnt1]=T[j];
    			for(int j=q[i].r;j>0;j-=lowbit(j)) rT[++cnt2]=T[j];
    			printf("%d
    ",mp[query(1,cnt,q[i].k)]);
    		}
    		else {
    			q[i].k=lower_bound(mp+1,mp+cnt+1,q[i].k)-mp;
    			add(q[i].l,-1);a[q[i].l]=q[i].k;add(q[i].l,1);
    		}
    	}
    	return 0;
    }
    

    2、 [CQOI2011]动态逆序对

    先用树状数组求一下总的逆序对

    然后每次就减去当前在 ([1,pos[x]-1]) 中大于 (x) 的数的个数和当前在 ([pos[x]+1,n]) 中小于 (x) 的数

    带修改主席树即可,时间复杂度 (O(nlog^2n)),空间复杂度 (O(nlog^2n))

    (Code Below:)

    #include <bits/stdc++.h>
    #define ll long long
    #define lowbit(x) ((x)&(-(x)))
    using namespace std;
    const int maxn=100000+10;
    int n,m,a[maxn],pos[maxn];ll ans;
    int T[maxn],lT[maxn],rT[maxn],L[maxn*400],R[maxn*400],sum[maxn*400],tot,cnt1,cnt2;
    
    inline int read(){
        register int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
        while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
        return (f==1)?x:-x;
    }
    
    namespace unorder_pair{
        struct Binary_Index_Tree{
            int c[maxn];
            void update(int x,int y){
                for(;x<=n;x+=lowbit(x)) c[x]+=y;
            }
            int sum(int x){
                int ans=0;
                for(;x;x-=lowbit(x)) ans+=c[x];
                return ans;
            }
        }T;
        ll solve(){
            ll ans=0;
            for(int i=n;i>=1;i--){
                ans+=T.sum(a[i]);
                T.update(a[i],1);
            }
            return ans;
        }
    }
    
    
    void update(int &now,int l,int r,int x,int v){
        if(!now) now=++tot;
        sum[now]+=v;
        if(l == r) return ;
        int mid=(l+r)>>1;
        if(x <= mid) update(L[now],l,mid,x,v);
        else update(R[now],mid+1,r,x,v);
    }
    
    void add(int x,int v){
        for(int i=x;i<=n;i+=lowbit(i))
            update(T[i],1,n,a[x],v);
    }
    
    int query(int l,int r,int v,int sta){
        if(l>r) return 0;
        cnt1=cnt2=0;
        for(int i=l-1;i>0;i-=lowbit(i)) lT[++cnt1]=T[i];
        for(int i=r;i>0;i-=lowbit(i)) rT[++cnt2]=T[i];
        l=1,r=n;int mid,ans=0;
        while(l!=r){
            mid=(l+r)>>1;
            if(mid < v){
                if(!sta){
                    for(int i=1;i<=cnt1;i++) ans-=sum[L[lT[i]]];
                    for(int i=1;i<=cnt2;i++) ans+=sum[L[rT[i]]];
                }
                for(int i=1;i<=cnt1;i++) lT[i]=R[lT[i]];
                for(int i=1;i<=cnt2;i++) rT[i]=R[rT[i]];
                l=mid+1;
            }
            else {
                if(sta){
                    for(int i=1;i<=cnt1;i++) ans-=sum[R[lT[i]]];
                    for(int i=1;i<=cnt2;i++) ans+=sum[R[rT[i]]];
                }
                for(int i=1;i<=cnt1;i++) lT[i]=L[lT[i]];
                for(int i=1;i<=cnt2;i++) rT[i]=L[rT[i]];
                r=mid;
            }
        }
        return ans;
    }
    
    int main()
    {
        n=read(),m=read();
        for(int i=1;i<=n;i++) a[i]=read(),pos[a[i]]=i;
        ans=unorder_pair::solve();
        for(int i=1;i<=n;i++) add(i,1);
        int x;
        for(int i=1;i<=m;i++){
            x=read();
            printf("%lld
    ",ans);
            ans-=(ll)query(1,pos[x]-1,x,1);
            ans-=(ll)query(pos[x]+1,n,x,0);
            add(pos[x],-1);
        }
        return 0;
    }
    
  • 相关阅读:
    java — 排序算法
    sping框架(3)— 使用spring容器
    java — 线程池
    一致性哈希算法(consistent hashing)
    linux进程调度
    滑动窗口协议
    final和static关键字
    II 3.1 连接到服务器
    intel-hadoop/HiBench流程分析----以贝叶斯算法为例
    Android开发学习之路--基于vitamio的视频播放器(二)
  • 原文地址:https://www.cnblogs.com/owencodeisking/p/9978768.html
Copyright © 2020-2023  润新知