分析
首先,需要注意求的是众数出现的次数,而不是众数是哪个(先开始看错写题一直报运行错误),对于我的做法而言,相对蒲公英反而少了很多码量。
可以先去做P4168蒲公英吧,数据范围小了很多,虽然感觉两者无太大区别。
首先将整个序列分为根号块,用 \(f[i][j]\) 表示从第 \(i\) 块到第 \(j\) 块众数出现的次数,这样我们每次找到一个询问时就可以先得出整块里的答案。
然后对于单个的怎么做呢?我们考虑 \(vector\)。用 \(v[i][j]\) 表示第 \(i\) 类的第 \(j\) 个数的位置。这样每次找到一个数时,例如我们从整块向左找到一个数,我们就在它对应的 \(vector\) 里找它向后延伸当前答案 \(mx\),如果其仍在所求区间内,则说明答案可以加一,用代码来表示一下。
if(v[s][pos[kl]+mx]<=kr)mx++;
//s为当前位置数的类型,kl是枚举到的左端点,mx是已经处理到的答案,kr是枚举到的右端点
这样就做完了,具体预处理的操作可以看代码,注释的很详细了。
#include<bits/stdc++.h>
using namespace std;
#define re register
inline void read(int &res)
{
res=0;
int f=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
res*=f;
}
int n,m;
int l,r;
int tot;
int flag[500005];
map<int,int>vis;
int fk[500005];
int cs[500005];
int f[710][710];
int s[710][710];
int bak[500005];
int fr[500005];
int pos[500005];
int mx;
int ks;
int cnt[600015];
vector<int>v[500005];
bool isf[500005],isb[500005];
int las;
int sqr;
signed main()
{
read(n);
read(m);
sqr=sqrt(n);
for(re int i=1;i<=n;i++)
{
fk[i]=i/sqr+1;
if(fk[i]>fk[i-1]) //块的分界线
{
ks++;
bak[ks-1]=i-1;//上一块的末位置
fr[ks]=i;//该块开头位置
isb[i-1]=isf[i]=1;//标记是否为起点和终点
}
}
bak[ks]=n;
isb[n]=1;
for(re int i=1; i<=n; i++)
{
read(cs[i]);
if(!vis[cs[i]])
{
vis[cs[i]]=++tot;//数值的标记
}
flag[i]=vis[cs[i]];//属于哪一类
v[flag[i]].push_back(i);//将其位置加入vector
pos[i]=v[flag[i]].size()-1;//该类型中其位置
}
for(re int i=1; i<=ks; i++)
{
int k=fr[i]-1;
mx=0;
memset(cnt,0,4*(tot+20));//各类型数字多少个
for(re int j=i; j<=ks; j++)//第i块到第j块一共的情况
{
s[i][j]=s[i][j-1];
while(k<bak[j])
{
++k;
cnt[flag[k]]++;
if(cnt[flag[k]]>mx)mx=cnt[flag[k]];
}
f[i][j]=mx;//众数的个数
}
}
while(m--)
{
read(l);
read(r);
l=l^las,r=r^las;
if(l>r)swap(l,r);
int ll=fk[l]+(isf[l]?0:1),rr=fk[r]-(isb[r]?0:1);//查找范围内整块是从第几块到第几块
if(ll>rr)//不包含一个整块,暴力枚举
{
mx=0;
memset(cnt,0,4*(tot+20));
for(int i=l;i<=r;i++)
{
cnt[flag[i]]++;
if(cnt[flag[i]]>mx)mx=cnt[flag[i]];
}
}
else//不同块,区间内部有完整块
{
mx=f[ll][rr];
int kl=fr[ll],kr=bak[rr];
while(kl>l)//上面给出解释了
{
int s=flag[--kl];
if(pos[kl]+mx>=v[s].size())continue;
if(v[s][pos[kl]+mx]<=kr)mx++;
}
while(kr<r)
{
int s=flag[++kr];
if(pos[kr]<mx)continue;
if(v[s][pos[kr]-mx]>=kl)++mx;
}
}
printf("%d\n",las=mx);
}
return 0;
}
//340 393