题解 P2617 Dynamic Rankings

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

题解 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='\n') {
    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('\n');
		}
		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)\)

题解 P2617 Dynamic Rankings

(orz BF)

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

上一篇:P2617 Dynamic Rankings【树状数组套主席树】


下一篇:13、华为 华三中小型企业网络架构搭建 【防火墙篇之安全策略部署】