[日常摸鱼]bzoj4358-莫队+线段树

bzoj4358-莫队+线段树

题意:给出一个长度为\(n\)的排列\(P(P_1,P_2,...P_n)\),以及\(m\)个询问。每次询问某个区间\([l,r]\)中,最长的值域

连续段长度(例如)。\(n,m\leq 5\times 10^4\)

题解:

(这题网上有挺多做法的,莫队+线段树/莫队+并查集/莫队+栈/KD-Tree,这里我原本是想找KD-Tree的题才找到这题的,但是没想到怎么做,用莫队做完去查了题解发现还需要一些前置知识,考虑到自己的学习进度好几天没往下推了,暂时还是不推支线了x,其他做法就先欠着吧x,KD树碰一碰就继续推主线去)

就想着先把题做出来再说…于是其实就能很自然地想到用莫队来处理询问,很自然地去考虑一边处理区间一边维护一个值域序列\(t[]\)\(t[i]\)表示\(i\)这个数是否在区间内,接着要求的“最长值域连续长度”就成了序列上最长连续的1的长度,很经典的线段树的题:每个结点除了答案以外维护一个左端点开始的最长连续1和右端点开始的最长连续1,接着类似分治那样来维护(具体见代码的update)

这样一来每次修改的代价都是\(O(\log n)\),莫队处理询问自带一个根号,最终复杂度是\(O(n\sqrt n \log n)\)的。

const int N=5e4+5;
const int M=240;
struct query{
	int l,r,idx;
}Q[N];
int n,m,T,bl[N],ans[N],a[N];
int tr[N<<2],ls[N<<2],rs[N<<2];
#define lson (node<<1)
#define rson (node<<1|1)
void update(int node,int l,int r){
	int mid=(l+r)>>1;
	tr[node]=max(tr[lson],max(tr[rson],ls[rson]+rs[lson]));
	ls[node]=ls[lson];rs[node]=rs[rson];
	if(ls[lson]==mid-l+1){
		ls[node]+=ls[rson];
		tr[node]=max(tr[node],ls[node]);
	}
	if(rs[rson]==r-mid){
		rs[node]+=rs[lson];
		tr[node]=max(tr[node],rs[node]);
	}
}
void modify(int node,int l,int r,int x,int v){
	if(l==r){
		tr[node]+=v;
		ls[node]=rs[node]=tr[node];
		return ;
	}
	int mid=(l+r)>>1;
	if(mid>=x)modify(lson,l,mid,x,v);
	else modify(rson,mid+1,r,x,v);
	update(node,l,r);
}
#undef lson
#undef rson
bool cmp1(query p,query q){
	if(bl[p.l]!=bl[q.l])return p.l<q.l;
	return p.r<q.r;
}
int main(){
	scanf("%d%d",&n,&m);
	T=sqrt(n);
	rep(i,1,n)bl[i]=(i-1)/T+1;
	rep(i,1,n)scanf("%d",&a[i]);
	rep(i,1,m)scanf("%d%d",&Q[i].l,&Q[i].r),Q[i].idx=i;
	sort(Q+1,Q+m+1,cmp1);
	int l,r;
	l=Q[1].l;r=l-1;
	rep(i,1,m)	{
		int ql=Q[i].l,qr=Q[i].r;
		while(l<ql){modify(1,1,n,a[l],-1);l++;}
		while(l>ql){l--;modify(1,1,n,a[l],1);}
		while(r<qr){r++;modify(1,1,n,a[r],1);}
		while(r>qr){modify(1,1,n,a[r],-1);r--;}
		ans[Q[i].idx]=tr[1];
	}
	rep(i,1,m)printf("%d\n",ans[i]);
	return 0;
}
posted @ 2021-02-28 11:13  yoshinow2001  阅读(53)  评论(0编辑  收藏  举报