题目链接:https://www.luogu.org/problemnew/show/P4168
题目大意:

思路一:

在线区间众数的分块做法比较多,这里提供一个思路:

首先离散化一下比较方便。


可以推导出:[L, R]的众数一定是整数块的众数,或者两个边块的数。

所以对于每次查询,众数的可能值最多有2sqrt(n)+1个

所以我们可以预处理f(i,j)表示第 i 块到第 j 块的众数(枚举 i 开个桶扫一遍)。

那么只要能快速得出一个数在某个区间内出现次数即可,每次只要比较至多2√n+1个元素的出现次数,这题就解决了。

由于没有修改,只要离散化以后,给每个数 x 开个vector,按顺序存下 x 出现的位置,每次询问 x 时把区间的左右端点放进对应 vector 二分一下即可。

根据均值不等式,可以算出分块大小大概是√(n/logn)

复杂度比较高,必须开O2才能过。

思路二:如果我们能够把整块区间求某个数的个数,复杂度降低到O(1)就完美了。

怎么降低呢?,用前缀和,s[i][j]表示:从1-第i块结束, 数字j出现的次数。

现在对于一个众数可能值,我们只要求出现它在整块的个数,和两个边块的次数就行了。

我们统计次数时,必须把值映射一下:因为用桶去统计,每次mem一下。桶的大小太大就是O(n)了, 只要2sqrt+1个就行了。

思路1:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
//由块号寻找第一个块元素的下标
#define LL(x) ((x-1)*Len+1)
LL read()
{
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int a[50005], b[50005], cut[50005], vis[50005];
int f[505][505];
map<int, int> mp;

vector<int> v[50005];
int n, m, Len;

void pre(int x)
{
    memset(cut, 0, sizeof(cut));
    int mx=0, ans=0;
    for(int i=LL(x); i<=n; i++)
    {
        cut[a[i]]++;
        int t=b[i];
        if(cut[a[i]]>mx||cut[a[i]]==mx&&a[i]<ans)
        {
            ans=a[i], mx=cut[a[i]];
        }
        f[x][t]=ans;
    }
}

int query(int L, int R, int x)
{
    int t=upper_bound(v[x].begin(),v[x].end(),R)-lower_bound(v[x].begin(),v[x].end(),L);
    return t;
}

int query(int L, int R)
{
    int ans, mx;
    ans=f[b[L]+1][b[R]-1];
    mx=query(L, R, ans);
    for(int i=L;i<=min(LL(b[L]+1)-1, R); i++)
    {
        int t=query(L, R, a[i]);
        if(t>mx||t==mx&&a[i]<ans)
        {
            ans=a[i]; mx=t;
        }
    }
    if(b[L]!=b[R])
    {
        for(int i=LL(b[R]); i<=R; i++)
        {
            int t=query(L, R, a[i]);
            if(t>mx||t==mx&&a[i]<ans)
            {
                ans=a[i]; mx=t;
            }
        }
    }

    return ans;
}

void build(int n)
{
    Len=n/sqrt(n);
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        b[i]=(i-1)/Len+1;
    }
    for(int i=1;i<=n;i++)
    {
        mp[a[i]]=1;
    }
    int pos=1;
    for(map<int, int>::iterator it=mp.begin(); it!=mp.end(); it++)
    {
        it->second=pos++;
        vis[pos-1]=it->first;
    }
    for(int i=1;i<=n;i++)
    {
        a[i]=mp[a[i]];
        v[a[i]].push_back(i);
    }
    for(int i=1;i<=b[n];i++)
    {
        pre(i);
    }
}

int main()
{
    n=read(), m=read();
    build(n);
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        int L=read(), R=read();
        L=(L+ans-1)%n+1, R=(R+ans-1)%n+1;
        if(L>R)
        {
            swap(L, R);
        }
        ans=vis[query(L, R)];
        printf("%d\n", ans);
    }

    return 0;
}

思路二:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
//由块号寻找第一个块元素的下标
#define LL(x) ((x-1)*Len+1)
LL read()
{
    LL x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int a[40050], b[40050], cut[40050], vis[40050];
int f[205][205];
int s[205][40050];
map<int, int> mp;
int n, m, Len;

void pre(int x)//初始化
{
    memset(cut, 0, sizeof(cut));
    int mx=0, ans=0;
    for(int i=LL(x); i<=n; i++)
    {
        cut[a[i]]++;
        int t=b[i];
        if(cut[a[i]]>mx||cut[a[i]]==mx&&a[i]<ans)
        {
            ans=a[i], mx=cut[a[i]];
        }
        f[x][t]=ans;
    }

    for(int i=1;i<LL(x+1);i++)
    {
        s[x][a[i]]++;
    }

}

int query(int L, int R, int x)//区间查询x出现的个数
{
    return s[b[R]][x]-s[b[L]-1][x];
}

int id[40050];
int BH(int L1, int R1, int L2, int R2)//边块处理
{
    int tot=1;
    for(int i=L1;i<=R1;i++)//初始化映射表
    {
        id[a[i]]=0;
    }
    for(int i=L2;i<=R2;i++)
    {
        id[a[i]]=0;
    }

    for(int i=L1;i<=R1;i++)//创建映射
    {
        if(id[a[i]]==0)
        {
            id[a[i]]=tot++;
        }
    }
    for(int i=L2;i<=R2;i++)
    {
        if(id[a[i]]==0)
        {
            id[a[i]]=tot++;
        }
    }
}

int query(int L, int R)
{
    int Size[505]={0};

    int ans=-1, mx=0;
    if(b[L]==b[R])
    {
        BH(L, R, 0, -1);
        for(int i=L;i<=R;i++)
        {
            Size[id[a[i]]]++;
            if(Size[id[a[i]]]>mx||Size[id[a[i]]]==mx&&ans>a[i])
            {
                ans=a[i]; mx=Size[id[a[i]]];
            }
        }
    }
    else
    {
        BH(L, LL(b[L]+1)-1, LL(b[R]), R);

        id[0]=f[b[L]+1][b[R]-1];//把区间众数加入桶
        Size[0]=s[b[R]-1][id[0]]-s[b[L]][id[0]];
        ans=id[0], mx=Size[0];

        for(int i=L;i<LL(b[L]+1);i++)
        {
            Size[id[a[i]]]++;
            int sum=s[b[R]-1][a[i]]-s[b[L]][a[i]];

            if(Size[id[a[i]]]+sum>mx||Size[id[a[i]]]+sum==mx&&ans>a[i])
            {
                ans=a[i]; mx=Size[id[a[i]]]+sum;
            }
        }
        for(int i=LL(b[R]);i<=R;i++)
        {
            Size[id[a[i]]]++;
            int sum=s[b[R]-1][a[i]]-s[b[L]][a[i]];
            if(Size[id[a[i]]]+sum>mx||Size[id[a[i]]]+sum==mx&&ans>a[i])
            {
                ans=a[i]; mx=Size[id[a[i]]]+sum;
            }
        }

    }

    return ans;
}

void build(int n)
{
    Len=n/sqrt(n);
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        b[i]=(i-1)/Len+1;
    }
    for(int i=1;i<=n;i++)
    {
        mp[a[i]]=1;
    }
    int pos=1;
    for(map<int, int>::iterator it=mp.begin(); it!=mp.end(); it++)
    {
        it->second=pos++;
        vis[pos-1]=it->first;
    }
    for(int i=1;i<=n;i++)
    {
        a[i]=mp[a[i]];
    }
    for(int i=1;i<=b[n];i++)
    {
        pre(i);
    }
}

int main()
{
    n=read(), m=read();
    build(n);
    int ans=0;
    for(int i=1;i<=m;i++)
    {
        int L=read(), R=read();
        L=(L+ans-1)%n+1, R=(R+ans-1)%n+1;
        if(L>R)
        {
            swap(L, R);
        }
        ans=vis[query(L, R)];
        printf("%d\n", ans);
    }

    return 0;
}

/* 10 5 2 1 2 1 1 2 3 3 3 1 */