• 题解 P2617 Dynamic Rankings


    一道 树状数组套权值线段数的模板题。(然而我刚开始用线段树套平衡树做了)

    (orz hzwer)

    题意

    给定一个含有 n 个数的序列 (a_1,a_2 dots a_n),需要支持两种操作:

    • Q l r k 表示查询下标在区间$ [l,r]$中的第 k 小的数
    • C x y 表示将 (a_x) 改为 y

    分析

    一道 树状数组套权值线段数的模板题。(然而我刚开始用线段树套平衡树做了)

    因为权值线段树资瓷前缀和相减,

    • 如果我们对每一个点都做一个前缀和(like 主席树),那么 单次修改 (O(nlogn)) ,查询 (O(logn))
    • TLE了
    • 如果不做前缀和(暴力(雾)) 单次查询 (O(n)) , 修改 (O(1))
    • 一样T飞
    • 于是我们想到有没有一种数据结构能资瓷一个具有前缀和性质的序列(如果把权值线段树看作一个元素的话)--------> 单点修改,求前缀和。
    • 树状数组
    • 于是就有了
    • 树状数组套权值树

    Code

    (我加了一个tmpa和tmps的去重)

    #include<set>
    #include<vector>
    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #define LL long long
    #define rint register int
    using namespace std;
    namespace FastIO
    {
    const int _SIZE = (1 << 21) + 1;
    char ibuf[_SIZE],obuf[_SIZE];
    char *iS,*iT;
    char c;
    char qu[55];
    char *oS=obuf,*oT=oS+_SIZE-1;
    bool _sign=false;
    int qr;
    // getchar
    #define gc() (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, _SIZE, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
    // print the remaining part
    inline void flush() 
    {
        fwrite(obuf,1,oS-obuf,stdout);
        oS=obuf;
        return;
    }
    // putchar
    inline void putc(const char &x) 
    {
        *oS++=x;
        if(oS==oT)
            flush();
        return;
    }
    inline char getc()
    {
    	return gc();
    }
    // input a signed integer
    template <class T>
    inline void read(T &x) 
    {
    	x=0;
    	_sign=false;
        for (c=gc();c<'0'||c>'9';c=gc())
            if (c=='-')
                _sign=true;
        for (;c>='0'&&c<='9';c=gc()) 
    		x=(x<<1)+(x<<3)+(c&15);
        x=(_sign) ? (~x+1) : x;
        return;
    }
    // print a signed integer
    template <class T>
    inline void print(T x,char ch='
    ') {
        if (!x) {
        	putc('0');
        	return;
    	}
        if (x<0)
            putc('-'),x=~x+1;
        while(x) qu[++qr]=x%10+'0',x/=10;
        while(qr) putc(qu[qr--]);
        putc(ch);
        return;
    }
    // no need to call flush at the end manually!
    struct Flusher_ 
    {
        ~Flusher_() { flush(); }
    }io_flusher_;
    }  // namespace io
    using FastIO::read;
    using FastIO::print;
    using FastIO::putc;
    using FastIO::getc;
    //=================================================
    const int N=1e5+5;
    struct Node
    {
    	int lc,rc;
    	unsigned int sum;
    }t[300*N];
    int pool=0;
    #define lowbit(x) ((x)&(-(x)))
    int n,m;
    int root[N];
    void Insert(int &now,const int &key,const int &l,const int &r)
    {
    	if(!now) now=++pool;
    	t[now].sum++;
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	if(key<=mid) Insert(t[now].lc,key,l,mid);
    	else Insert(t[now].rc,key,mid+1,r);
    	return;
    }
    void erase(int now,const int &key,const int &l,const int &r)
    {
    	t[now].sum--;
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	if(key<=mid) erase(t[now].lc,key,l,mid);
    	else erase(t[now].rc,key,mid+1,r);
    	return;
    }
    set<int> tmp;
    int tmpa[N];
    int tmps[N];
    int querykth(unsigned rank,const int &l,const int &r)
    {
    	if(l==r) return l;
    	rint i;
    	unsigned int temp=0;
    	for(i=1;i<=tmpa[0];i++) temp+=t[t[tmpa[i]].lc].sum;
    	for(i=1;i<=tmps[0];i++) temp-=t[t[tmps[i]].lc].sum;
    	int mid=(l+r)>>1;
    	if(rank<=temp) {
    		for(i=1;i<=tmpa[0];i++) tmpa[i]=t[tmpa[i]].lc;
    		for(i=1;i<=tmps[0];i++) tmps[i]=t[tmps[i]].lc;
    		return querykth(rank,l,mid);
    	}
    	else {
    		for(i=1;i<=tmpa[0];i++) tmpa[i]=t[tmpa[i]].rc;
    		for(i=1;i<=tmps[0];i++) tmps[i]=t[tmps[i]].rc;
    		return querykth(rank-temp,mid+1,r);
    	}
    }
    vector<int> v;
    inline int findkth(int l,int r,int rank)
    {
    	rint i;
    	tmp.clear();
    	tmpa[0]=tmps[0]=0;
    	for(i=r;i>=1;i-=lowbit(i)) 
    		tmp.insert(i);
    	for(i=l-1;i>=1;i-=lowbit(i)) {
    		if(tmp.count(i)) 
    			tmp.erase(i);
    		else tmps[++tmps[0]]=root[i];
    	}
    	for(set<int>::iterator it=tmp.begin();it!=tmp.end();it++)
    		tmpa[++tmpa[0]]=root[*it];
    	return querykth(rank,1,v.size());
    }
    inline void Add(int pos,const int &key)
    {
    	for(;pos<=n;pos+=lowbit(pos)) 
    		Insert(root[pos],key,1,v.size());
    	return;
    }
    inline void Sub(int pos,const int &key)
    {
    	for(;pos<=n;pos+=lowbit(pos)) 
    		erase(root[pos],key,1,v.size());
    	return;
    }
    struct quiz
    {
    	int opt;
    	int x,y,z;
    }q[N];
    int a[N];
    int main()
    {
    //	freopen("1.in","r",stdin);
    	rint i;
    	register char opt;
    	read(n); read(m);
    	for(i=1;i<=n;i++) {
    		read(a[i]);
    		v.push_back(a[i]);
    	}	
    	for(i=1;i<=m;i++) {
    		for(opt=getc();opt!='Q'&&opt!='C';opt=getc());
    		if(opt=='Q') q[i].opt=1;
    		else q[i].opt=2;
    		read(q[i].x); read(q[i].y);
    		if(q[i].opt==1) read(q[i].z); 
    		else v.push_back(q[i].y);
    	}
    	sort(v.begin(),v.end());
    	v.erase(unique(v.begin(),v.end()),v.end());
    	for(i=1;i<=n;i++) 
    		a[i]=(int)(lower_bound(v.begin(),v.end(),a[i])-v.begin())+1;
    	for(i=1;i<=m;i++) 
    		if(q[i].opt==2) 
    			q[i].y=(int)(lower_bound(v.begin(),v.end(),q[i].y)-v.begin())+1;
    	for(i=1;i<=n;i++) 
    		Add(i,a[i]);
    	for(i=1;i<=m;i++) {
    		if(q[i].opt==1) 
    			print(v[findkth(q[i].x,q[i].y,q[i].z)-1]);
    		else Sub(q[i].x,a[q[i].x]),Add(q[i].x,q[i].y),a[q[i].x]=q[i].y;
    	}
    	return 0;
    }
    

    时间复杂度 (O((n+m)logn))

    空间复杂度 (O(nlog^{2}n))

    (attention !)

    • (log_{2}100000 = 17)(大概), (17^{2}=289)
    • 所以至少要开 289*N 单位的空间(我一般开300倍)
    • 另外,(root[i]) 不要写成 (i) 了,分清楚 不同的 l,r 代表什么(是序列区间还是权值区间)

    最后贴上我50分的反面教材

    #include<vector>
    #include<cstdio>
    #include<cstdlib>
    #include<iostream>
    #include<algorithm>
    #define LL long long
    #define rint register int
    using namespace std;
    namespace FastIO
    {
    const int _SIZE = (1 << 21) + 1;
    char ibuf[_SIZE],obuf[_SIZE];
    char *iS,*iT;
    char c;
    char qu[55];
    char *oS=obuf,*oT=oS+_SIZE-1;
    bool _sign=false;
    int qr;
    // getchar
    #define gc() (iS == iT ? (iT = (iS = ibuf) + fread(ibuf, 1, _SIZE, stdin), (iS == iT ? EOF : *iS++)) : *iS++)
    // print the remaining part
    inline void flush() 
    {
        fwrite(obuf,1,oS-obuf,stdout);
        oS=obuf;
        return;
    }
    // putchar
    inline void putc(const char &x) 
    {
        *oS++=x;
        if(oS==oT)
            flush();
        return;
    }
    inline char getc()
    {
    	return gc();
    }
    // input a signed integer
    template <class T>
    inline void read(T &x) 
    {
    	x=0;
    	_sign=false;
        for (c=gc();c<'0'||c>'9';c=gc())
            if (c=='-')
                _sign=true;
        for (;c>='0'&&c<='9';c=gc()) 
    		x=(x<<1)+(x<<3)+(c&15);
        x=(_sign) ? (~x+1) : x;
        return;
    }
    // print a signed integer
    template <class T>
    inline void print(T x) {
        if (!x) {
        	putc('0');
        	return;
    	}
        if (x<0)
            putc('-'),x=~x+1;
        while(x) qu[++qr]=x%10+'0',x/=10;
        while(qr) putc(qu[qr--]);
        return;
    }
    // no need to call flush at the end manually!
    struct Flusher_ 
    {
        ~Flusher_() { flush(); }
    }io_flusher_;
    }  // namespace io
    using FastIO::read;
    using FastIO::print;
    using FastIO::putc;
    using FastIO::getc;
    //=================================================
    const int N=1e5+5;
    struct Node
    {
    	int lc,rc;
    	int size;
    	int val,pri;
    }t[20*N];
    int pool=0;
    #define newnode(x) (pool++,t[pool].lc=t[pool].lc=0,t[pool].size=1,t[pool].val=x,t[pool].pri=rand(),pool) 
    #define update(x) t[x].size=t[t[x].lc].size+t[t[x].rc].size+1
    void split(int now,const int &key,int &x,int &y) //<= >
    {
    	if(now==0) 
    		return x=y=0,void();
    	return (t[now].val<=key) 
    		? (x=now,split(t[now].rc,key,t[now].rc,y),update(now),void())
    		: (y=now,split(t[now].lc,key,x,t[now].lc),update(now),void());
    }
    int merge(int x,int y)
    {
    	if(x==0||y==0)
    		return x+y;
    	return (t[x].pri>t[y].pri) 
    		? (t[x].rc=merge(t[x].rc,y),update(x),x)
    		: (t[y].lc=merge(x,t[y].lc),update(y),y); 
    }
    inline void insert(int &rt,const int &key)
    {
    	int x,y;
    	split(rt,key,x,y);
    	rt=merge(merge(x,newnode(key)),y);
    	return;
    }
    inline void erase(int &rt,const int &key)
    {
    	int x,y,z;
    	split(rt,key-1,x,z);
    	split(z,key,y,z);
    	y=merge(t[y].lc,t[y].rc);
    	z=merge(y,z);
    	rt=merge(x,z);
    	return;
    }
    inline int findrank(int &rt,const int &key)
    {
    	int x,y,z;
    	split(rt,key-1,x,y);
    	z=t[x].size;
    	rt=merge(x,y);
    	return z;
    }
    //====================================================
    int root[4*N];
    int a[N];
    void edit(int now,const int &pos,const int &key,const int &l,const int &r)
    {
    	erase(root[now],a[pos]);
    	insert(root[now],key);
    	if(l==r) return;
    	int mid=(l+r)>>1;
    	if(pos<=mid) edit(now<<1,pos,key,l,mid);
    	else edit(now<<1|1,pos,key,mid+1,r);
    	return;
    }
    int getrank(int now,const int &key,const int &x,const int &y,const int &l,const int &r)
    {
    	if(r<x||l>y) return 0;
    	if(x<=l&&r<=y) 
    		return findrank(root[now],key);
    	int mid=(l+r)>>1;
    	return getrank(now<<1,key,x,y,l,mid)+getrank(now<<1|1,key,x,y,mid+1,r);
    }
    int n,m;
    struct quiz
    {
    	int opt;
    	int x,y,z;
    }q[N];
    vector<int> v;
    int main()
    {
    //	freopen("1.in","r",stdin);
    	srand(20040524);
    	rint i;
    	register char opt;
    	int L,R,mid;
    	read(n); read(m);
    	for(i=1;i<=n;i++) {
    		read(a[i]);
    		v.push_back(a[i]);
    	}	
    	for(i=1;i<=m;i++) {
    		for(opt=getc();opt!='Q'&&opt!='C';opt=getc());
    		if(opt=='Q') q[i].opt=1;
    		else q[i].opt=2;
    		read(q[i].x); read(q[i].y);
    		if(q[i].opt==1) read(q[i].z); 
    		else v.push_back(q[i].y);
    	}
    	sort(v.begin(),v.end());
    	v.erase(unique(v.begin(),v.end()),v.end());
    	for(i=1;i<=n;i++) 
    		a[i]=(int)(lower_bound(v.begin(),v.end(),a[i])-v.begin())+1;
    	for(i=1;i<=m;i++) 
    		if(q[i].opt==2) 
    			q[i].y=(int)(lower_bound(v.begin(),v.end(),q[i].y)-v.begin())+1;
    	for(i=1;i<=n;i++) 
    		edit(1,i,a[i],1,n);
    	for(i=1;i<=m;i++) {
    		if(q[i].opt==1) {
    			L=1,R=(int)v.size()+1;
    			while(L+1<R) {
    				mid=(L+R)>>1;
    				if(getrank(1,mid,q[i].x,q[i].y,1,n)+1<=q[i].z) L=mid;
    				else R=mid;
    			}
    			print(v[L-1]),putc('
    ');
    		}
    		else edit(1,q[i].x,q[i].y,1,n),a[q[i].x]=q[i].y;
    	}
    	return 0;
    }
    

    时间复杂度:(O(nlog^{2}n+mlog^{3}n))

    空间复杂度: (O(nlogn))

    (orz BF)

    (我确实不喜欢线段树套平衡树,因为其复杂度太不均衡了)

  • 相关阅读:
    【Netty学习】 ChannelInitializer 学习
    【Netty学习】Netty 4.0.x版本和Flex 4.6配合
    Spring框架学习
    【JS教程23】jquery链式调用
    【JS教程22】jquery特殊效果
    【JS教程21】数组及操作方法
    【JS教程20】jquery动画
    【JS教程19】jquery绑定click事件
    【JS教程18】jquery样式操作
    【JS教程17】jquery选择器
  • 原文地址:https://www.cnblogs.com/cjl-world/p/13191627.html
Copyright © 2020-2023  润新知