bzoj4358-permu

给出一个\(n\)的排列,\(m\)次询问\((l,r)\),求其中最长值域连续段的长度。

\(n,m\le 5\times 10^4\)

分析

这个问题看似很难做,但它不要求在线,所以可以考虑离线然后使用莫队算法。

记录值域上每个位置左右两边各有多少个连续存在于当前区间的位置。在中间插入一个位置的时候,修改左右两边就行了。但如果修改左右两边的全部位置,时间明显无保证,又注意到如果只有添加的话只用修改这个连续区间的左右端点,我们考虑如何进行无删除的莫队。

不妨设\(n,m\) 同阶。依然把询问区间分为\(B\)块,对询问进行排序,左端点所在块为第一关键字,右端点为第二关键字。对于块内询问,我们暴力处理它,一次为\(O(B)\) 。对于\(l\)在一个块中,\(r\)不在这个块中的情况,我们可以发现\(r\)是递增的,所以可以不用删除。那\(l\)怎么办呢?我们就让它每次都从块尾往左移动到需要的位置,再移回来——由于一次只用改最多两个地方,我们可以记录每一次\(l\)移动导致的修改,回退一下就可以了。这种情况下右端点的移动次数为\(O(\frac{n^2}{B})\),左端点的移动次数为\(O(nB)\),总复杂度为\(O(2nB+\frac{n^2}{B})\),取\(B=\sqrt \frac{n}{2}\) 即可取到最优 \(O(2n\sqrt {2n})\)

这题还有一个奇妙做法,把询问看成一个点,那么位置为\(i\)的数只会被以\((i,i)\)为右下角的矩形中的询问包含。于是我们用把\(1\cdots n\) 一个个处理,用KD-Tree来维护历史最大值即可。复杂度为\(O(n\sqrt n)\)

代码

这是莫队的做法。

#include<cstdio>
#include<cctype>
#include<cstring>
#include<cmath>
#include<algorithm>
#define M(x) memset(x,0,(sizeof x[0])*(n+1))
using namespace std;
inline char nchar() {
	const int bufl=1<<20;
	static char buf[bufl],*a,*b;
	return a==b && (b=(a=buf)+fread(buf,1,bufl,stdin),a==b)?EOF:*a++;
}
inline int read() {
	int x=0,f=1;
	char c=nchar();
	for (;!isdigit(c);c=nchar()) if (c=='-') f=-1;
	for (;isdigit(c);c=nchar()) x=x*10+c-'0';
	return x*f;
}
inline void write(int x) {
	if (!x) puts("0"); else {
		static char s[13];
		int tot=0;
		for (;x;x/=10) s[++tot]='0'+x%10;
		while (tot) putchar(s[tot--]);
		puts("");
	}
}
const int maxn=5e4+1;
int n,m,size,a[maxn],block[maxn],eb[maxn],ans[maxn],theans;
int lef[maxn],rig[maxn];
void Max(int &x,int y) {x=max(x,y);}
struct query {
	int l,r,id;
	inline bool operator < (const query &a) const {return block[l]==block[a.l]?r<a.r:block[l]<block[a.l];}
} b[maxn];
void inc(int p,int &theans) {
	int x=a[p];
	int l=lef[x-1],r=rig[x+1];
	if (l && r) Max(theans,rig[x-l]=lef[x+r]=l+r+1); else 
	if (l && !r) Max(theans,lef[x]=++rig[x-l]),Max(theans,rig[x]=1); else 
	if (!l && r) Max(theans,rig[x]=++lef[x+r]),Max(theans,lef[x]=1); else 
	Max(theans,rig[x]=lef[x]=1);
}
void brute(query &c) {
	theans=0;
	int &l=c.l,&r=c.r,&id=c.id;
	for (int i=l;i<=r;++i) inc(i,theans);
	ans[id]=theans;
	for (int i=l;i<=r;++i) lef[a[i]]=rig[a[i]]=0;
}
int gol(int p,int dist,int theans) {
	int x=a[p];
	int l=lef[x-1],r=rig[x+1];
	int bef[]={lef[x],rig[x],lef[x+r],rig[x-l]};
	inc(p,theans);
	int ret;
	ret=(p==dist?theans:gol(p-1,dist,theans));
	lef[x]=bef[0],rig[x]=bef[1],lef[x+r]=bef[2],rig[x-l]=bef[3];
	return ret;
}
int main() {
#ifndef ONLINE_JUDGE
	freopen("test.in","r",stdin);
#endif
	n=read(),m=read();
	int size=max(1,(int)sqrt((double)n/2));
	for (int i=1;i<=n;++i) block[i]=(i-1)/size+1;
	for (int i=1;i<=n;++i) if (block[i]!=block[i+1]) eb[block[i]]=i;
	for (int i=1;i<=n;++i) a[i]=read();
	for (int i=1;i<=m;++i) {
		int l=read(),r=read();
		b[i]=(query){l,r,i};
	}
	sort(b+1,b+m+1);
	for (int i=1,j;i<=m;i=j+1) {
		M(lef),M(rig);
		int wh=block[b[i].l];
		for (j=i-1;j<m && block[b[j+1].l]==wh && block[b[j+1].r]==wh;++j);
		for (int k=i;k<=j;++k) brute(b[k]);
		int st=j+1;
		for (;j<m && block[b[j+1].l]==wh;++j);
		int ew=eb[wh],nowr=ew;
		theans=0;
		for (int k=st;k<=j;++k) {
			int &l=b[k].l,&r=b[k].r,&id=b[k].id;
			while (nowr<r) inc(++nowr,theans);
			ans[id]=gol(ew,l,theans);
		}
	}
	for_each(ans+1,ans+m+1,write);
	return 0;
}
posted @ 2017-07-21 16:44  permui  阅读(358)  评论(0编辑  收藏  举报