[日常摸鱼]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;
}