带插入区间K小值 - 解题报告

这题真毒,BZOJ过了,Luogu没过(请求增长时限QwQ)

题意(三种操作):

  • 插入一个数到数列中
  • 修改数列中一个数的值
  • 询问区间第\(k\)

强制在线。

然后可怜的我只会 \(O(n \log^3 n)\) 的做法QAQ……
这道题可以树套树,由于需要动态插点,所以外层树平衡树;由于需要询问区间\(k\)小值,所以内层树可以是权值线段树(不嫌烦可以写平衡树),维护外层树所在子树的权值信息,显然如果外层树需要旋转操作,维护内层树将相当麻烦,所以我建议外层树写替罪羊树
对于插入和修改操作,可以根据二叉搜索树的做法进行,同时子树不平衡时暴力重构。
对于查询操作,用线段树上查询区间的方法用vector存储需要访问的区间,显然最多有\(\log n\)个区间,再二分答案计算(类似主席树的查询操作)。
目前时间的瓶颈在于重构部分暴力点权插入线段树需要\(O(n \log^3 n)\),使得Luogu上\(TLE\),BZOJ上\(AC\)
然后是代码(\(TLE\)):

#include<bits/stdc++.h>
#define ratio 4
using namespace std;
inline int read() {
	register int tmp=0;register char c=getchar(); while(c<'0'||c>'9')	c=getchar();
	while(c>='0'&&c<='9')	tmp=(tmp<<1)+(tmp<<3)+(c^48),c=getchar(); return tmp;
}
inline void print(int x) { if(x>9)	print(x/10); putchar(x%10+'0'); }

const int N=70010;
int n,m,a[N],ans;
vector <int> q,ttt;
int root;
int Tot=0;
struct ST {
	int l,r,s;
	ST(int L,int R,int S):l(L),r(R),s(S) {}
	ST() {}
};ST tr[20000010]; int Sta[20000010],Top=0;
int tot=0;
struct P {
	int l,r,val,rt,sz;
	P(int L,int R,int V,int Rt,int S):l(L),r(R),val(V),rt(Rt),sz(S) {}
	P() {}
};P t[N];
int sta[N],top=0;

inline int Min(const int x,const int y) { return x<y?	x:y; }
inline int Max(const int x,const int y) { return x>y?	x:y; }
inline int st_node() {
	if(!Top) return ++Tot; --Top; return Sta[Top+1];
}
void clean(int &u) {
	if(tr[u].l)	clean(tr[u].l); if(tr[u].r)	clean(tr[u].r);
	Sta[++Top]=u,tr[u]=ST(0,0,0),u=0;
}
void st_insert(int &u,int l,int r,int x,int w) {
	if(!u)	u=st_node(); tr[u].s+=w;
	if(l>=r) { if(!tr[u].s)	clean(u); return ; }
	int mid=(l+r)>>1;
	x<=mid?	st_insert(tr[u].l,l,mid,x,w):
			st_insert(tr[u].r,mid+1,r,x,w);
	if(!tr[u].s)	clean(u);
}
void debug(int u,int Tab) {
	if(t[u].l)	debug(t[u].l,Tab+1);
	for(int i=1;i<Tab;i++)	printf("     |");
	printf(" %d\n",t[u].val);
	if(t[u].r)	debug(t[u].r,Tab+1);
}
inline bool Bad(int u) {
	return (double)t[t[u].l].sz>ratio*(double)t[t[u].r].sz||
	       (double)t[t[u].r].sz>ratio*(double)t[t[u].l].sz;
}
inline int sgt_node(int l,int r,int v,int sz) {
	t[sta[top]]=P(l,r,v,0,sz),--top; return sta[top+1];
}
void sgt_init(int &u,int l,int r) {
	int mid=(l+r)>>1; u=sgt_node(0,0,a[mid],r-l+1);
	for(int i=l;i<=r;i++)	st_insert(t[u].rt,0,70000,a[i],1);
	if(l<mid)	sgt_init(t[u].l,l,mid-1);
	if(mid<r)	sgt_init(t[u].r,mid+1,r);
}
void ask(int u,int l,int r,int L,int R) {
	if(L>R||l>r)	return ;
	if(l==L&&r==R) { q.push_back(u);return ; }
	int mid=t[t[u].l].sz+l;
	if(L<=mid&&mid<=R)	ttt.push_back(t[u].val);
	if(R<mid)	ask(t[u].l,l,mid-1,L,R);
	else if(L>mid)	ask(t[u].r,mid+1,r,L,R);
	else	ask(t[u].l,l,mid-1,L,mid-1),ask(t[u].r,mid+1,r,mid+1,R);
}
int solve(int l,int r,int k) {
	ask(root,1,t[root].sz,l,r); int L=0,R=70000,mid,sum;
	for(int i=0;i<q.size();i++)	q[i]=t[q[i]].rt;
//	printf("ASK : ");
//	for(int i=0;i<q.size();i++)	printf(" %d",tr[q[i]].s);printf("\n");
//	for(int i=0;i<ttt.size();i++)	printf(" 1",ttt[i]);printf("\n");
	while(L<R) {
		mid=(L+R)>>1,sum=0;
		for(int i=0;i<q.size();i++)	sum+=tr[tr[q[i]].l].s;
		for(int i=0;i<ttt.size();i++)	if(ttt[i]<=mid)	++sum;
//		printf("MWHAKIOI : %d %d\n",mid,sum);
		if(sum>=k) {
			R=mid; for(int i=0;i<q.size();i++)	q[i]=tr[q[i]].l;
		}
		else {
			k-=sum,L=mid+1;
			for(int i=0;i<q.size();i++)	q[i]=tr[q[i]].r;
			for(int i=0;i<ttt.size();i++)
				if(ttt[i]<=mid)	ttt[i]=70010;
		}
	}
	q.clear(),ttt.clear(); return R;
}
int sgt_find(int u,int k) {
	if(t[t[u].l].sz+1==k)	return t[u].val;
	if(k<=t[t[u].l].sz)	return sgt_find(t[u].l,k);
	return sgt_find(t[u].r,k-t[t[u].l].sz-1);
}
void sgt_mdy(int u,int k,int Old,int New) {
	st_insert(t[u].rt,0,70000,Old,-1),
	st_insert(t[u].rt,0,70000,New,1);
	if(t[t[u].l].sz+1==k) { t[u].val=New;return ; }
	k<=t[t[u].l].sz?	sgt_mdy(t[u].l,k,Old,New):
	sgt_mdy(t[u].r,k-t[t[u].l].sz-1,Old,New);
}
void dfs(int &u) {
	if(t[u].l)	dfs(t[u].l);
	a[++n]=t[u].val,clean(t[u].rt),sta[++top]=u;
	if(t[u].r)	dfs(t[u].r);
	u=0;
}
void rebuild(int &u) { n=0,dfs(u),sgt_init(u,1,n); }
void sgt_insert(int &u,int x,int w) {
	if(!u) {
		u=sgt_node(0,0,w,1),st_insert(t[u].rt,0,70000,w,1);
		return ;
	}
	st_insert(t[u].rt,0,70000,w,1),++t[u].sz;
	if(x<=t[t[u].l].sz)	sgt_insert(t[u].l,x,w);
	else	sgt_insert(t[u].r,x-t[t[u].l].sz-1,w);
	if(!Bad(u)) {
		if(Bad(t[u].l))	rebuild(t[u].l);
		if(Bad(t[u].r))	rebuild(t[u].r);
	}
}
int main() {
	n=read();
	for(register int i=1;i<=n;++i)	a[i]=read(); m=read();
	for(register int i=1;i<N;++i)	sta[++top]=i;
	sgt_init(root,1,n);
//	printf("Array : \n"),debug(root,1),printf("\n");
	for(;m;--m) {
		register char opt=getchar();
		while(opt<'A'||opt>'Z')	opt=getchar();
		if(opt=='Q') {
			register int l=read()^ans,r=read()^ans,k=read()^ans;
			ans=solve(l,r,k),print(ans),putchar(10);
		}
		else if(opt=='M') {
			register int x=read()^ans,y=read()^ans,z;
			z=sgt_find(root,x),sgt_mdy(root,x,z,y);
		}
		else {
			register int x=read()^ans,y=read()^ans;
			sgt_insert(root,x-1,y); if(Bad(root))	rebuild(root);
		}
//		printf("Array : \n"),debug(root,1),printf("\n");
	}
	return 0;
}

目前想到的解决方式:

  • 线段树合并
  • leafy tree 实现重量平衡树(线段树合并)

(然而这么做好像有问题)

被卡的代码:

#include<bits/stdc++.h>
#define ratio 3
using namespace std;
inline int read() {
	register int tmp=0;register char c=getchar();
	while(c<'0'||c>'9')	c=getchar();
	while(c>='0'&&c<='9')	tmp=(tmp<<1)+(tmp<<3)+(c^48),c=getchar();
	return tmp;
}
inline void print(int x) { if(x>9)	print(x/10); putchar(x%10+'0'); }

const int N=70010;
int n,m,a[N],ans;
vector <int> q,ttt;
int root;
int Tot=0;
struct ST {
	int l,r,s;
	ST(int L,int R,int S):l(L),r(R),s(S) {}
	ST() {}
};ST tr[20000010]; int Sta[20000010],Top=0;
int tot=0;
struct P {
	int l,r,val,rt,sz;
	P(int L,int R,int V,int Rt,int S):l(L),r(R),val(V),rt(Rt),sz(S) {}
	P() {}
};P t[N];
int sta[N],top=0;

inline int Min(const int &x,const int &y) { return x<y?	x:y; }
inline int Max(const int &x,const int &y) { return x>y?	x:y; }
inline int st_node() {
	if(!Top) return ++Tot; --Top; return Sta[Top+1];
}
void clean(int &u) {
	if(tr[u].l)	clean(tr[u].l); if(tr[u].r)	clean(tr[u].r);
	Sta[++Top]=u,tr[u]=ST(0,0,0),u=0;
}
void st_insert(int &u,int l,int r,int x,int w) {
	if(!u)	u=st_node(); tr[u].s+=w;
	if(l>=r) { if(!tr[u].s)	clean(u); return ; }
	int mid=(l+r)>>1;
	x<=mid?	st_insert(tr[u].l,l,mid,x,w):
			st_insert(tr[u].r,mid+1,r,x,w);
	if(!tr[u].s)	clean(u);
}
void debug(int u,int Tab) {
	if(t[u].l)	debug(t[u].l,Tab+1);
	for(int i=1;i<Tab;i++)	printf("     |");
	printf(" %d\n",t[u].val);
	if(t[u].r)	debug(t[u].r,Tab+1);
}
inline bool Bad(int u) {
	return (double)t[t[u].l].sz>ratio*(double)t[t[u].r].sz||
	       (double)t[t[u].r].sz>ratio*(double)t[t[u].l].sz;
}
inline int sgt_node(int l,int r,int v,int sz) {
	t[sta[top]]=P(l,r,v,0,sz),--top; return sta[top+1];
}
void Merge(int &u,int l,int r,int L,int R) {
	if(!l&&!r)	return ;
	u=st_node(),tr[u].s=tr[l].s+tr[r].s;
	if(l==r)	return ;
	int mid=(L+R)>>1;
	Merge(tr[u].l,tr[l].l,tr[r].l,L,mid),
	Merge(tr[u].r,tr[l].r,tr[r].r,mid+1,r);
}
void sgt_init(int &u,int l,int r) {
	int mid=(l+r)>>1; u=sgt_node(0,0,a[mid],r-l+1);
	if(l<mid)	sgt_init(t[u].l,l,mid-1);
	if(mid<r)	sgt_init(t[u].r,mid+1,r);
	Merge(t[u].rt,t[t[u].l].rt,t[t[u].r].rt,0,70000);
	st_insert(t[u].rt,0,70000,a[mid],1);
}
void ask(int u,int l,int r,int L,int R) {
	if(L>R||l>r)	return ;
	if(l==L&&r==R) { q.push_back(u);return ; }
	int mid=t[t[u].l].sz+l;
	if(L<=mid&&mid<=R)	ttt.push_back(t[u].val);
	if(R<mid)	ask(t[u].l,l,mid-1,L,R);
	else if(L>mid)	ask(t[u].r,mid+1,r,L,R);
	else	ask(t[u].l,l,mid-1,L,mid-1),ask(t[u].r,mid+1,r,mid+1,R);
}
int solve(int l,int r,int k) {
	ask(root,1,t[root].sz,l,r); int L=0,R=70000,mid,sum;
	for(int i=0;i<q.size();i++)	q[i]=t[q[i]].rt;
//	printf("ASK : ");
//	for(int i=0;i<q.size();i++)	printf(" %d",tr[q[i]].s);printf("\n");
//	for(int i=0;i<ttt.size();i++)	printf(" 1",ttt[i]);printf("\n");
	while(L<R) {
		mid=(L+R)>>1,sum=0;
		for(int i=0;i<q.size();i++)	sum+=tr[tr[q[i]].l].s;
		for(int i=0;i<ttt.size();i++)	if(ttt[i]<=mid)	++sum;
//		printf("MWHAKIOI : %d %d\n",mid,sum);
		if(sum>=k) {
			R=mid; for(int i=0;i<q.size();i++)	q[i]=tr[q[i]].l;
		}
		else {
			k-=sum,L=mid+1;
			for(int i=0;i<q.size();i++)	q[i]=tr[q[i]].r;
			for(int i=0;i<ttt.size();i++)
				if(ttt[i]<=mid)	ttt[i]=70010;
		}
	}
	q.clear(),ttt.clear(); return R;
}
int sgt_find(int u,int k) {
	if(t[t[u].l].sz+1==k)	return t[u].val;
	if(k<=t[t[u].l].sz)	return sgt_find(t[u].l,k);
	return sgt_find(t[u].r,k-t[t[u].l].sz-1);
}
void sgt_mdy(int u,int k,int Old,int New) {
	st_insert(t[u].rt,0,70000,Old,-1),
	st_insert(t[u].rt,0,70000,New,1);
	if(t[t[u].l].sz+1==k) { t[u].val=New;return ; }
	k<=t[t[u].l].sz?	sgt_mdy(t[u].l,k,Old,New):
	sgt_mdy(t[u].r,k-t[t[u].l].sz-1,Old,New);
}
void dfs(int &u) {
	if(t[u].l)	dfs(t[u].l);
	a[++n]=t[u].val,clean(t[u].rt),sta[++top]=u;
	if(t[u].r)	dfs(t[u].r);
	u=0;
}
void rebuild(int &u) { n=0,dfs(u),sgt_init(u,1,n); }
void sgt_insert(int &u,int x,int w) {
	if(!u) {
		u=sgt_node(0,0,w,1),st_insert(t[u].rt,0,70000,w,1);
		return ;
	}
	st_insert(t[u].rt,0,70000,w,1),++t[u].sz;
	if(x<=t[t[u].l].sz)	sgt_insert(t[u].l,x,w);
	else	sgt_insert(t[u].r,x-t[t[u].l].sz-1,w);
	if(!Bad(u)) {
		if(Bad(t[u].l))	rebuild(t[u].l);
		if(Bad(t[u].r))	rebuild(t[u].r);
	}
}
int main() {
	n=read();
	for(register int i=1;i<=n;++i)	a[i]=read(); m=read();
	for(register int i=1;i<N;++i)	sta[++top]=i;
	sgt_init(root,1,n);
//	printf("Array : \n"),debug(root,1),printf("\n");
	for(;m;--m) {
		register char opt=getchar();
		while(opt<'A'||opt>'Z')	opt=getchar();
		if(opt=='Q') {
			register int l=read()^ans,r=read()^ans,k=read()^ans;
			ans=solve(l,r,k),print(ans),putchar(10);
		}
		else if(opt=='M') {
			register int x=read()^ans,y=read()^ans,z;
			z=sgt_find(root,x),sgt_mdy(root,x,z,y);
		}
		else {
			register int x=read()^ans,y=read()^ans;
			sgt_insert(root,x-1,y); if(Bad(root))	rebuild(root);
		}
//		printf("Array : \n"),debug(root,1),printf("\n");
	}
	return 0;
}

然后仅剩下的思路是块状链表,若能做到\(O(n \sqrt {n})\) 同样理论可过。
我尝试去敲块状链表,由于常数优秀,跑得比\(O(n \log^2 n)\)还快,Luogu上过了60%的数据。听说某位大神用块状链表卡进去了,吾辈表示无能为力。
写题没有必要做对,写题的目的是锻炼思维和解题方式,留下思考的痕迹,这样写题才有意义。
最后我在这里留下块状链表写法的代码。

#include<bits/stdc++.h>
#define ONLINE_JUDGE
#define re register
using namespace std;
const int N=70000,BLOSIZE=1100,MXBLO=2*N/BLOSIZE;
int n,m;

struct P {
	int a[3*BLOSIZE+100],b[3*BLOSIZE+100],sz,to;
	void reset() {
		for(re int i=1;i<=sz;++i)	b[i]=a[i];
		sort(b+1,b+sz+1);
	}
};P t[MXBLO+100];
int tot=0;

inline int read() {
	re int x=0;re char c=getchar();
	while(c<'0'||c>'9')	c=getchar();
	while(c>='0'&&c<='9')	x=(x<<1)+(x<<3)+(c^48),c=getchar();
	return x;
}
void print(int x) { if(x>9)	print(x/10); putchar(x%10+'0'); }
inline void insert(int x,int w) {
	re int u,tt;
	for(u=0;u!=-1&&x>t[u].sz;u=t[u].to)	x-=t[u].sz;
	for(re int i=t[u].sz;i>=x;--i)	t[u].a[i+1]=t[u].a[i];
	t[u].a[x]=w,++t[u].sz;
	if(t[u].sz>=2*BLOSIZE) {
		int nu=++tot; t[nu].to=t[u].to,t[u].to=nu;
		t[nu].sz=t[u].sz-BLOSIZE;
		for(re int i=BLOSIZE+1;i<=t[u].sz;++i)
			t[nu].a[i-BLOSIZE]=t[nu].b[i-BLOSIZE]=t[u].a[i];
		for(re int i=1;i<=BLOSIZE;++i) t[u].b[i]=t[u].a[i];
		t[u].sz=BLOSIZE;
		sort(t[nu].b+1,t[nu].b+t[nu].sz+1),
		sort(t[u].b+1,t[u].b+t[u].sz+1); return ;
	}
	tt=t[u].sz-1;
	for(;tt;--tt) {
		if(t[u].b[tt]>w)	t[u].b[tt+1]=t[u].b[tt];
		else	break;
	}
	t[u].b[tt+1]=w;
}
void modify(int x,int w) {
	re int u,tt;
	for(u=0;u!=-1&&x>t[u].sz;u=t[u].to)	x-=t[u].sz;
	for(re int i=1;i<=t[u].sz;++i)
		if(t[u].b[i]==t[u].a[x]) {
			for(re int j=i;j<t[u].sz;++j)
				t[u].b[j]=t[u].b[j+1];
			t[u].b[t[u].sz--]=0;break;
		}
	t[u].a[x]=w,tt=t[u].sz;
	for(;tt;--tt) {
		if(t[u].b[tt]>w) t[u].b[tt+1]=t[u].b[tt];
		else	break;
	}
	t[u].b[tt+1]=w,++t[u].sz;
}
int query(int x,int y,int w) {
	re int u,v,sum=0,l,r,mid,tt;
	for(u=0;u!=-1&&x>t[u].sz;u=t[u].to)	x-=t[u].sz;
	for(v=0;v!=-1&&y>t[v].sz;v=t[v].to)	y-=t[v].sz;
	if(u==v) {
		for(int i=x;i<=y;i++) if(t[u].a[i]<w) ++sum;
		return sum;
	}
	for(re int i=x;i<=t[u].sz;++i) if(t[u].a[i]<w) ++sum;
	for(re int i=1;i<=y;++i) if(t[v].a[i]<w) ++sum;
	for(re int i=t[u].to;i!=v;i=t[i].to) {
		l=1,r=t[i].sz,tt=0;
		while(l<=r) {
			mid=(l+r)>>1;
			t[i].b[mid]<w?	(tt=mid,l=mid+1):r=mid-1;
		}
		sum+=tt;
	}
	return sum;
}
void init() {
	t[0].to=-1,t[0].sz=0,n=read(); int cnt=BLOSIZE,Old=0;
	for(re int i=1;i<=n;++i) {
		if(cnt==BLOSIZE) {
			int u=++tot;
			t[u].to=t[Old].to,t[Old].to=u,
			sort(t[Old].b+1,t[Old].b+t[Old].sz+1);
			Old=u,cnt=0;
		}
		t[Old].a[++t[Old].sz]=read(),
		t[Old].b[t[Old].sz]=t[Old].a[t[Old].sz],
		++cnt;
	}
	if(cnt==BLOSIZE) {
		int u=++tot;
		t[u].to=t[Old].to,t[Old].to=u,
		sort(t[Old].b+1,t[Old].b+t[Old].sz+1);
		Old=u,cnt=0;
	}
	t[Old].a[++t[Old].sz]=70000,
	t[Old].b[t[Old].sz]=t[Old].a[t[Old].sz],
	sort(t[Old].b+1,t[Old].b+t[Old].sz+1);
	m=read();
}
void debug() {
	for(int i=0;i!=-1;i=t[i].to) {
		printf(" ( [%d]",i);
		for(int j=1;j<=t[i].sz;++j)
			printf(" %d",t[i].b[j]);
		printf(" )");
	}
	printf("\n");
}
int main() {
	#ifndef ONLINE_JUDGE
	freopen("Dynamic Kth in range.in","r",stdin);
	freopen("Dynamic Kth in range.out","w",stdout);
	int cur=clock();
	#endif
	char c; int x,y,k,ans=0,l,r,mid; init();
	for(;m;--m) {
		c=getchar(); while(c<'A'||c>'Z')	c=getchar();
		switch(c) {
			case 'Q':
				x=read()^ans,y=read()^ans,k=read()^ans;
				l=0,r=70000;
				while(l<=r) {
					mid=(l+r)>>1;
					query(x,y,mid)<k?
						(l=mid+1,ans=mid):r=mid-1;
				}
				print(ans),putchar(10);break;
			case 'I':
				x=read()^ans,y=read()^ans,insert(x,y);break;
			default:	x=read()^ans,y=read()^ans,modify(x,y);
		}
		#ifndef ONLINE_JUDGE
		ans=0;
		#endif
	}
	#ifndef ONLINE_JUDGE
	printf(">>> %d ms.",clock()-cur);
	fclose(stdin),fclose(stdout);
	#endif
	return 0;
}
posted @ 2018-12-24 20:19  daniel14311531  阅读(441)  评论(0编辑  收藏  举报