【題意】給定長度爲n的排列,m次詢問區間[L,R]的最長連續值域。n<=50000。算法
【算法】莫隊算法數組
【題解】考慮莫隊維護增長一個數的信息:設up[x]表示數值x往上延伸的最大長度,down[x]表示數值x往下延伸的最大長度。ide
增長一個數x時,up[x]=up[x+1]+1,down[x]=down[x-1]+1。令t=up[x]+down[x]+1,能夠用於更新答案。spa
同時,增長x後會影響到x所在連續區間最大數和最小數,中間的數字不會影響後面的答案(由於只考慮加數,中間的數字雖然改變但不會被調用),因此有:code
down[x+up[x]-1] = up[x-down[x]+1] = tblog
回顧莫隊算法的複雜度分析。莫隊算法按左端點分塊,塊內按右端點排序。假設塊大小爲B,左端點複雜度O(B*q),右端點複雜度O(n/B*n)。排序
實際上,咱們只須要保證每次詢問左端點複雜度爲O(B),每一塊詢問右端點複雜度爲O(n)就能夠了。get
既然只須要每次詢問左端點複雜度爲O(B),乾脆不用刪點的操做實現,改爲暴力加點實現。string
記塊的右端點的r,對於同一塊的詢問,將>r的右端點逐漸增長向右擴展並累計。對於每次詢問,暴力增長塊內的部分至詢問左端點處,用棧記錄修改,作完後清除。it
複雜度O(n√m)。
#include<cstdio> #include<cmath> #include<cstring> #include<algorithm> using namespace std; const int maxn=50010; int a[maxn],ANS[maxn],s1[maxn*2],s2[maxn*2],up[maxn],down[maxn],top,ans,answer; int n,m; struct cyc{int l,r,q,id;}b[maxn]; bool cmp(cyc x,cyc y){return x.q^y.q?x.q<y.q:x.r<y.r;} void modify(int x){ up[x]=up[x+1]+1; down[x]=down[x-1]+1; int t=up[x]+down[x]-1; s1[++top]=x+up[x]-1;s2[top]=down[s1[top]]; s1[++top]=x-down[x]+1;s2[top]=up[s1[top]]; down[s1[top-1]]=up[s1[top]]=t; ans=max(ans,t); } int main(){ scanf("%d%d",&n,&m); int Q=(int)(1.0*n/sqrt(m)); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=m;i++){scanf("%d%d",&b[i].l,&b[i].r);b[i].q=(b[i].l-1)/Q+1;b[i].id=i;} sort(b+1,b+m+1,cmp); int r=0,t=0; for(int i=1;i<=m;i++){ if(b[i].q!=b[i-1].q){ memset(up,0,sizeof(up)); memset(down,0,sizeof(down)); t=r=b[i].q*Q;answer=0; } ans=0;top=0; while(b[i].r>r)modify(a[++r]); top=0;//forget... answer=ans=max(answer,ans); for(int j=b[i].l;j<=min(t,b[i].r);j++)modify(a[j]); ANS[b[i].id]=ans; for(int j=top;j>=1;j--)if(j%2)down[s1[j]]=s2[j];else up[s1[j]]=s2[j]; for(int j=b[i].l;j<=min(t,b[i].r);j++)up[a[j]]=down[a[j]]=0; } for(int i=1;i<=m;i++)printf("%d\n",ANS[i]); return 0; }
注意:棧數組開2倍
還有想着後面要記得寫的東西能夠先寫到草稿紙上,否則後面忘了……GG