题解[P5048 Yuno loves sqrt technology III]
题目链接
做此题前建议先做这题
虽然同样是求区间众数,但那题数据范围小得多,空间限制也较松,这题还得另辟蹊径。
考虑像那题一样分块,初始时用 \(O(n^{\frac{3}{2}})\) 的时间处理出两两块中众数的出现次数。
而由于空间限制不能处理出每个数在每个块之前出现总次数,以处理散块。
但事实上我们并不需要得知每个值真正的出现次数,只要看它能不能在区间中出现次数比当前答案大。
下以左散块为例说明具体操作:
首先从左散块的右端开始向 \(l\) 移动,设当前位置上的值为 \(x\) ,已得答案为 \(ans\) ,
那当前点的值能更新答案,就必须在区间内 \(x\) 之后 \(x\) 的个数比 \(ans\) 大。
于是就可以把原序列离散化,然后对每个值存入这些值所在序列中位置,可以用指针实现,常数小。
同时记录序列中每个位置 \(i\) 上的值 \(x\) 之前 \(x\) 出现过的次数 \(rev_i\) (包括这个位置)。
记 \(pos_{x,i}\) 为第 \(i\) 个值为 \(x\) 的数的位置,\(cnt_x\) 为值为 \(x\) 的数的总出现次数。
那么当前值能更新答案的条件是 \(rev_i+ans\leq cnt_x\) 并且 \(pos_{x,rev_i+ans}\leq y\)
满足条件时 \(ans\) 便可加一。
由于是从左散块的右端开始向 \(l\) 移动,扩展范围逐渐变大,能保证正确性(略有不同与其他题解,但常数更小)
而对于右散块同样从其左端点开始向 \(r\) 移,能更新的条件便是:
\(rev_i > ans\) 并且 \(pos_{x,rev_i-ans}\geq l\)
同样由于计算区间是逐渐扩大,正确性依然能保证。
时间复杂度: \(O(n^{\frac{3}{2}})\) 空间复杂度:\(O(n)\)
代码:(这样做常数十分小,一点也不卡常) 若不是我强制在线写错好几次就能一遍过了
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10,NN=710;
int n,m,x,y,nn,_n,sn,ans,last;char ch;
int a[N],b[N],id[N],l[N],r[N],res[NN][NN],cnt[N],tmp[N],rev[N],*pos[N];
inline void read(int &x){
x=0;ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
}
inline void write(int x){if(x>=10)write(x/10);putchar('0'+x%10);}
inline void inquiry(int x,int y){
ans=res[id[x]+1][id[y]-1];register int i;
if(id[x]==id[y]){
for(i=x;i<=y;++i)ans=max(ans,++tmp[a[i]]);
for(i=x;i<=y;++i)--tmp[a[i]];
}
else {
for(i=r[id[x]];i>=x;--i)if(rev[i]+ans<=cnt[a[i]])if(pos[a[i]][rev[i]+ans]<=y)++ans;
for(i=l[id[y]];i<=y;++i)if(rev[i]>ans)if(pos[a[i]][rev[i]-ans]>=x)++ans;
}
}
main(){
read(n),read(m);nn=sqrt(n);sn=(n-1)/nn+1;register int i,j,k;
for(i=1;i<=n;++i)read(a[i]),b[i]=a[i],id[i]=(i-1)/nn+1;
for(i=1;i<=n;++i)r[id[i]]=i,l[id[n-i+1]]=n-i+1;
sort(b+1,b+n+1);_n=unique(b+1,b+n+1)-b-1;
for(i=1;i<=n;++i)a[i]=lower_bound(b+1,b+_n+1,a[i])-b,++cnt[a[i]];
for(i=1;i<=_n;++i)pos[i]=new int [cnt[i]+1];
for(i=1;i<=n;++i)pos[a[i]][++tmp[a[i]]]=i,rev[i]=tmp[a[i]];
for(i=1;i<=sn;++i){
for(j=1;j<=_n;++j)tmp[j]=0;
for(j=i;j<=sn;++j){
res[i][j]=res[i][j-1];
for(k=l[j];k<=r[j];++k)res[i][j]=max(res[i][j],++tmp[a[k]]);
}
}
for(j=1;j<=_n;++j)tmp[j]=0;
while(m--){
read(x),read(y);x^=last,y^=last;
inquiry(x,y);write(last=ans);putchar('\n');
}
}