分析

首先,需要注意求的是众数出现的次数,而不是众数是哪个(先开始看错写题一直报运行错误),对于我的做法而言,相对蒲公英反而少了很多码量。

可以先去做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
posted on 2021-07-21 16:45  漠寒·  阅读(25)  评论(0编辑  收藏  举报