看到这题就想到区间众数,然后又因为在线,就想到了分块的做法,再看一看数据范围1e5,显然可做,然后就找了找区间众数的代码,改了点细节,说几个修改的重点,首先因为数的范围最大是1e9,所以肯定需要离散化,其次块中记录的最大值应改为如题目所述的那样,为每个数乘以数出现的次数的最大值,最后且最关键的地方就是对边角的处理,在区间众数中,遍历左边角的时候,是将当前数的下标往右从ans起开始拓展,而在这题中,应该从当前数的下标往右从ans/a[i]个开始拓展(ans为中间块中的最大值,会根据边角的遍历而不断更新),右边角同理。(如若以上前置知识还未了解,移步https://www.luogu.com.cn/problem/P5048 ,先做做区间众数,学习一下分块,这题就迎刃而解啦!)
分享一下代码(代码中有详细注释)

#pragma GCC optimize(2)
#include <bits/stdc++.h> 
#include <memory>
using namespace std;
#define sf(a) scanf("%d",&a)
#define pf(a) printf("%d",a)
#define ford(i,a,n) for(i=a;i<=n;i++)
#define forb(i,n,a) for(i=n;i>=a;i--)
#define mst(a,b) memset(a,b,sizeof(a))
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
#define pb push_back
#define ddd cout<<"ddd"<<endl
#define maxn 100010
#define inf 0x7fffffff
//以上宏定义为个人码风习惯,如若影响阅读,十分抱歉 
#define siz 317  //每个块的大小 
const ll mod=1e9+7;
ll mx[410][410];//mx[i][j]就是第i个块到第j个块的最大值 
ll a[maxn];//离散后的数组 
int n,m,blocks;//数组大小,询问次数,块大小 
ll b[maxn];//离散前的数组 
ll lef[410],righ[410];//每个块的左边界和右边界 
ll book[maxn];//记录某个数的出现次数 
int num[maxn];//标记当前下标属于哪个块 
void init()
{
    blocks=(n-1)/siz+1;// 计算总共有多少块 
    int i;
    ford(i,1,blocks)
    {
        lef[i]=righ[i-1]+1;
        righ[i]=i*siz;
    }//计算每个块的左边界和右边界 
    righ[blocks]=n;
    ford(i,1,blocks)
    {
        mst(book,0);//把标记数组初始化 
        int j;
        ford(j,lef[i],righ[i])
        num[j]=i;//记录当前下标属于哪个块 
        ford(j,i,blocks)//遍历i到n,计算mx[i][j],j从i到n,每次最多遍历n个,最多遍历根号n次,所以此时复杂度为nsqrt(n) 
        {
            int k;
            mx[i][j]=mx[i][j-1];
            ford(k,lef[j],righ[j])//遍历 
            mx[i][j]=max(mx[i][j],b[a[k]]*(++book[a[k]]));
        }
    }
}
ll c[maxn];
vector<int>pos[maxn];//分块思想比较关键的地方来了,记录某个数出现的所有下标(按递增顺序记录) 
int rpos[maxn];//记录当前下标在 *当前下标的数* 的vector中的下标 (可能有点拗口,比如3这个数在下标为3,6,9这三个位置出现过,那么rpos3=0,rpos6=1,rpos9=2) 
int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin>>n>>m;
    int i;
    map<ll,int>mp;//离散化用的,懒狗专用STL 
    ford(i,1,n)
    {
        cin>>a[i];
        c[i]=a[i];//离散化用的 
    }
    sort(c+1,c+n+1);//离散化 (可用unique替代,个人习惯) 
    c[0]=-1;
    int now=0;
    ford(i,1,n)
    {
        if(c[i]!=c[i-1])
        {
            b[++now]=c[i];//从离散化的数回到原数就是通过b数组 
            mp[c[i]]=now;//离散化 
        }
    }
    ford(i,1,n)
    {
        a[i]=mp[a[i]];//把原数离散化 
        pos[a[i]].pb(i);//把下标记录到vector中 
        rpos[i]=pos[a[i]].size()-1;//意义如上 
    }
    init();
    ll ans=0; 
    mst(book,0);//分块的时候有用到book数组,所以此时重新初始化 
    while(m--)
    {
        ll l,r;
        cin>>l>>r;
        l^=ans;//如题 
        r^=ans;
        l=l%n+1;
        r=r%n+1;
        if(l>r)//如题 
        swap(l,r);
        ans=0;//重置最大值 
        if(num[l]==num[r])//如果左右边界在同一个块中,直接暴力,块大小为sqrt(n),所以复杂度可以接受 
        {
            ford(i,l,r) ans=max(ans,b[a[i]]*(++book[a[i]]));
            ford(i,l,r) book[a[i]]=0;//初始化,以免影响之后操作 
        }
        else
        {
            ans=mx[num[l]+1][num[r]-1];//把除了边角的部分先记录下来 
            ford(i,l,righ[num[l]])
            {
                int now=rpos[i];//求出当前下标在vector中的下标 
                if(b[a[i]]==0)//避免除0错误 
                continue;
                ll temp=ans/b[a[i]];//求出如果要比当前最大值大的所需最小数量 
                while(now+temp<pos[a[i]].size()&&pos[a[i]][now+temp]<=r)//分块思想最重要的地方,查询当前数之后的第temp个数的下标是否在r范围内,如果是的话继续增加temp,看看最多能增加多少 
                {
                    temp++;
                    ans=max(ans,b[a[i]]*temp);//求出最大值 
                }
            }
            ford(i,lef[num[r]],r)//意义同左边角,同理可得 
            {
                int now=rpos[i];
                if(b[a[i]]==0)
                continue;
                ll temp=ans/b[a[i]];
                while(now-temp>=0&&pos[a[i]][now-temp]>=l)
                {
                    temp++;
                    ans=max(ans,b[a[i]]*temp);
                }
            }
            //关于temp的暴力为什么会在sqrt(n)内,因为每次枚举完temp之后,答案是一直在增加的,那么下一次的起点就更加靠后,需要遍历的次数也变小了 
        }
        cout<<ans<<endl;
    }
}