题目大意
有一个长度为n的序列$a_1,a_2,……,a_n$,每次给出一个区间[l,r],求在区间内两个相等的数的最远距离($max(j-i,满足a_i==a_j且lle i,j le r)$)
思路:
分块将序列分成sqrt(n)块
预处理出每个数在每个块之后出现的最早位置和在每个块之前出现的最晚位置O(msqrt(n))
然后就可以按块进行区间DP,求出所有块之间的最大值 O(nsqrt(n))
答案就是max(f[L][R](两个数都在[L,R]里面的答案)O(1)
两个数都在[L,R]外面的答案,O(sqrt(n))
一个数在[L,R]里面,一个在外面的答案 O(sqrt(n)) )
总复杂度O(nsqrt(n))
具体实现看代码
#include<cstdio> #include<cmath> #include<algorithm> using namespace std; #define maxn 100005 #define maxs 350 #define inf 0x3fffff int block[maxn],a[maxn],size,f[maxs][maxs],pre[maxn][maxs],nex[maxn][maxs],mi[maxn]; int main(){ // freopen("1.in","r",stdin); int n,m,q,l,r;scanf("%d%d%d",&n,&m,&q);size=sqrt(n); for(int i=1;i<=n;i++)scanf("%d",a+i),block[i]=(i-1)/size+1; //预处理出每个数在每个块之后出现的最早位置和在每个块之前出现的最晚位置O(msqrt(n)) for(int i=1;i<=n;i++)nex[a[i]][block[i]]=i; for(int i=n;i>=1;i--)pre[a[i]][block[i]]=i; for(int i=1;i<=m;i++){ pre[i][block[n]+1]=inf; for(int j=block[n];j>=1;j--){ if(!pre[i][j])pre[i][j]=inf; pre[i][j]=min(pre[i][j],pre[i][j+1]); } for(int j=1;j<=block[n];j++){ nex[i][j]=max(nex[i][j],nex[i][j-1]); } } //按块进行区间DP,求出所有块之间的最大值 for(int i=block[n];i>=1;i--){ for(int j=i;j<=block[n];j++){ f[i][j]=max(f[i][j-1],f[i+1][j]); for(int k=(i-1)*size+1,l=i*size;k<=l;k++){ f[i][j]=max(f[i][j],nex[a[k]][j]-k); } } } for(int i=0;i<q;i++){ scanf("%d%d",&l,&r); int L=block[l]+1,R=block[r]-1,ans=f[L][R]; //求出左端点在[L,R]外,右端点在[L,R]里面的答案 for(int j=l;block[j]==block[l];j++)if(!mi[a[j]])mi[a[j]]=j,ans=max(ans,nex[a[j]][R]-j); for(int j=r;block[j]==block[r];j--){ ans=max(ans,j-(mi[a[j]]?mi[a[j]]:j));//求出两个端点都在[L,R]外的答案 ans=max(ans,j-pre[a[j]][L]);//求出右端点在[L,R]外,左端点在[L,R]里面的答案 } for(int j=l;block[j]==block[l];j++)mi[a[j]]=0; printf("%d ",ans); } return 0; }