前置知识
分块。
普通莫队
首先来看一道例题:
HH的项链
问题描述
HH有一串由各种漂亮的贝壳组成的项链。HH相信不同的贝壳会带来好运,所以每次散步 完后,他都会随意取出一段贝壳,思考它们所表达的含义。
HH不断地收集新的贝壳,因此, 他的项链变得越来越长。有一天,他突然提出了一个问题:某一段贝壳中,包含了多少种不同 的贝壳?这个问题很难回答。。。因为项链实在是太长了。于是,他只好求助睿智的你,来解 决这个问题。
输入格式
第一行:一个整数N,表示项链的长度。
第二行:N个整数,表示依次表示项链中贝壳的编号(编号为1到1000000之间的整数)。
第三行:一个整数M,表示HH询问的个数。
接下来M行:每行两个整数,L和R(1 ≤ L ≤ R ≤ N),表示询问的区间。
输出格式
M行,每行一个整数,依次表示询问对应的答案。
样例输入 1 复制
6
1 2 3 4 3 5
3
1 2
3 5
2 6
样例输出 1 复制
2
2
4
样例输入 2 复制
10
6 4 6 2 3 4 6 2 4 6
5
4 9
3 7
9 10
2 10
5 7
样例输出 2 复制
4
4
2
4
3
提示
对于20%的数据,N ≤ 100,M ≤ 1000;
对于40%的数据,N ≤ 3000,M ≤ 200000;
对于100%的数据,N ≤ 50000,M ≤ 200000。
————————————————————————————————————————————————————————
当然这道题可以线段树过...
考虑如果用暴力,那么这样的时间复杂度就是nm的,而莫队算法就是一种离线的暴力优化算法
首先莫队算法是针对区间问题求解且尽量可以离线的算法
莫队必须要满足一定的条件:
如果我们一直到在[L,R]这段区间的答案,那么必须要能够1至logn步算出[L,R+1],[L-1,R],[L+1,R]的答案(这里最好计算是O(1)的,比较好做。。。)
满足这样条件后,我们就可以进行莫队算法了
首先先将询问区间排序(关键字等一会再说)
然后用两个指针l,r最开始都指向0,然后对于每个询问,就直接移动l,r指针,使之移动到对应的位置,当然边移必须可以边算当前区间的答案。
这样做时间复杂度显然未降,因此这里的排序就显得很重要了
以这道题为例:把1-n进行分块,块长即为根号n,那么就可以计算出每一个点是属于哪一个块的。然后排序时先按左端点排,具体为按左端点所处的块的编号从小到大排序,如果编号相同,那么就按右端点从小到大排序。
那么移动的时候这样对于同一个块,l指针最多会移动根号n次,时间为O(根号N)r只会从小到大移动,因此是递增的,时间为O(N)
,l每一次移到另一个块,r最多会移动到n即时间复杂度O(N)
#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e6 + 4;
inline char GetChar(){
static char buf[10001],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,10001,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int &n){
short f=1;
long long x=0;
char c=GetChar();
while(isdigit(c)==false){
if(c=='-'){
f=-1;
}
c=GetChar();
}
while(isdigit(c)==true){
x=((x<<3)+(x<<1)+(c^48));
c=GetChar();
}
n=x*f;
}
int n , m , cnt[MAXN] , ans[MAXN], a[MAXN] , belong[MAXN];
struct node{
int num , l , r;
friend bool operator < ( node a , node b ){
return belong[a.l] != belong[b.l] ? (belong[a.l] < belong[b.l]) : (a.r < b.r);
}
}ask[MAXN];
int main(){
Read( n );
for( int i = 1 ; i <= n ; i ++ ){
Read( a[i] );
}
int len = sqrt( n );
for( int i = 1 ; i <= n ; i ++ ){
belong[i] = ( i - 1 ) / len + 1;
}
Read( m );
for( int i = 1 ; i <= m ; i ++ ){
Read( ask[i].l );Read( ask[i].r );
ask[i].num = i;
}
sort( ask + 1 , ask + 1 + m );
int l = 0 , r = 0 , sum = 0;
for( int i = 1 ; i <= m ; i ++ ){
int L = ask[i].l , R = ask[i].r;
while( r < R ) {
cnt[a[++r]] ++;
if( cnt[a[r]] == 1 ) sum ++;
}
while( r > R ){
cnt[a[r--]] --;
if( !cnt[a[r+1]] ) sum --;
}
while( l < L ){
cnt[a[l++]] --;
if( !cnt[a[l-1]] ) sum --;
}
while( l > L ){
cnt[a[--l]] ++;
if( cnt[a[l]] == 1 ) sum ++;
}
//printf( "%d\n" , sum );
ans[ask[i].num] = sum;
}
for( int i = 1 ; i <= m ; i ++ )
printf( "%d\n" , ans[i] );
return 0;
}
当然,这里排序的时候还可以优化一下,即如果块的编号是奇数,则r从小到大,否则r从大到小,那么r就不用回溯了
带修莫队
数颜色
E数颜色 | ||
|
问题描述
墨墨购买了一套N支彩色画笔(其中有些颜色可能相同),摆成一排,你需要回答墨墨的提问。墨墨会像你发布如下指令:
1、Q L R代表询问你从第L支画笔到第R支画笔中共有几种不同颜色的画笔。
2、R P Col 把第P支画笔替换为颜色Col。
为了满足墨墨的要求,你知道你需要干什么了吗?
输入格式
第1行两个整数N,M,分别代表初始画笔的数量以及墨墨会做的事情的个数。
第2行N个整数,分别代表初始画笔排中第i支画笔的颜色。
第3行到第2+M行,每行分别代表墨墨会做的一件事情,格式见题干部分。
输出格式
对于每一个Query的询问,你需要在对应的行中给出一个数字,代表第L支画笔到第R支画笔中共有几种不同颜色的画笔。
样例输入 复制
6 5
1 2 3 4 5 5
Q 1 4
Q 2 6
R 1 2
Q 1 4
Q 2 6
样例输出 复制
4
4
3
4
提示
对于100%的数据,N≤10000,M≤10000,所有的输入数据中出现的所有整数均大于等于1且不超过10^6。
如果没有修改,那么就很简单,但是如果修改就有点麻烦。回想当时莫队设了二维(l,r),而带修莫队就是要设三维(l,r,tim)
前二维含义是一样的,第三维是指这个询问区间之前经过了多少次修改,那么其实也可以O(1)求出(l,r,tim+1)与(l,r,tim-1)
那么相当于多加一个指针,在tim上进行移动即可,注意修改后值要swap
#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 133340;
const int MAXC = 1e6 + 3;
inline char GetChar(){
static char buf[10001],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,10001,stdin),p1==p2)?EOF:*p1++;
}
inline void Read(int &n){
short f=1;
long long x=0;
char c=GetChar();
while(isdigit(c)==false){
if(c=='-'){
f=-1;
}
c=GetChar();
}
while(isdigit(c)==true){
x=((x<<3)+(x<<1)+(c^48));
c=GetChar();
}
n=x*f;
}
int n , m , cnt[MAXC] , ans[MAXN], a[MAXN] , belong[MAXN] , numq , numc;
struct node{
int num , l , r , tim;
friend bool operator < ( node a , node b ){
if( belong[a.l] ^ belong[b.l] ) return belong[a.l] < belong[b.l];
if( belong[a.r] ^ belong[b.r] ) return belong[a.r] < belong[b.r];
return a.tim < b.tim;
}
}ask[MAXN];
struct edge{
int id , x;
}col[MAXN];
int main(){
//Read( n );Read( m );
scanf( "%d%d" , &n , &m );
for( int i = 1 ; i <= n ; i ++ ){
scanf( "%d" , &a[i] );
}
int len = pow( n , 2.0 / 3.0 );
for( int i = 1 ; i <= n ; i ++ ){
belong[i] = ( i - 1 ) / len + 1;
}
for( int i = 1 ; i <= m ; i ++ ){
char s[3];int x , y;
scanf( "%s" , s );
scanf( "%d%d" , &x , &y );
if( s[0] == 'Q' )
ask[++numq].l = x , ask[numq].r = y , ask[numq].tim = numc , ask[numq].num = numq;
else{
col[++numc].id = x , col[numc].x = y;
}
}
sort( ask + 1 , ask + 1 + numq );
int l = 0 , r = 0 , sum = 0 , now = 0 ;
for( int i = 1 ; i <= numq ; i ++ ){
int L = ask[i].l , R = ask[i].r , T = ask[i].tim;
while(l < L) sum -= !--cnt[a[l++]];
while(l > L) sum += !cnt[a[--l]]++;
while(r < R) sum += !cnt[a[++r]]++;
while(r > R) sum -= !--cnt[a[r--]];
while( now < T ){
if( col[++now].id >= l && col[now].id <= r ){
cnt[a[col[now].id]] --;
if( !cnt[a[col[now].id]] ) sum --;
cnt[col[now].x] ++;
if( cnt[col[now].x] == 1) sum ++;
}
swap( col[now].x , a[col[now].id] );
}
while( now > T ){
if( col[now].id >= l && col[now].id <= r ){
cnt[a[col[now].id]] --;
if( !cnt[a[col[now].id]] ) sum --;
cnt[col[now].x] ++;
if( cnt[col[now].x] == 1 ) sum ++;
}
swap( col[now].x , a[col[now].id] );
now --;
}
ans[ask[i].num] = sum;
}
for( int i = 1 ; i <= numq ; i ++ )
printf( "%d\n" , ans[i] );
return 0;
}
树上莫队
前置知识:欧拉序
例题:(不知道取什么名字)
问题描述
给定一个n个节点的树,每个节点表示一个整数,问u到v的路径上有多少个不同的整数。
输入格式
第一行有两个整数n和m(n=40000,m=100000)。 第二行有n个整数。第i个整数表示第i个节点表示的整数。 在接下来的n-1行中,每行包含两个整数u v,描述一条边(u,v)。 在接下来的m行中,每一行包含两个整数u v,询问u到v的路径上有多少个不同的整数。
输出格式
对于每个询问,输出结果。
这里需要引入一个新的知识,欧拉序,我们把欧拉序求出来之后,就变成了线性,而现在的区间就是欧拉序中,如果LCA在它们之中,那么左端点就是它们第一次出现在欧拉序的位置中的较小值,右端点是它们第一次出现在欧拉序的位置中的较大值。否则左端点就是两个点它们第二次出现在欧拉序的位置中的较小值,右端点是第一次出现在欧拉序的位置中的较大值。正确性显然。然后就可以开始做了
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 100003;
int id[MAXN] , top[MAXN] , oula[MAXN] ,flag[MAXN] , fa[MAXN] , dep[MAXN] , ncnt , ocnt , siz[MAXN] , zson[MAXN] , fo[MAXN];
int n , m , belong[MAXN] , ans[MAXN] , fo2[MAXN];
struct node{
int num , l , r , a , b;
friend bool operator < ( node a , node b ){
return ( belong[a.l] ^ belong[b.l] ) ? belong[a.l] < belong[b.l] : ( ( belong[a.l] &1 ) ? (a.r < b.r) : (a.r > b.r) );
}
}ask[MAXN];
int a[MAXN] , root , lc[MAXN] , cnt[MAXN];
int s[MAXN];
struct edge{
int x , num;
friend bool operator < ( edge a , edge b ){
return a.x < b.x;
}
}b[MAXN];
int bcnt;
vector<int>G[MAXN];
inline void dfs( int x , int f ){
oula[++ocnt] = x;siz[x] = 1;fo[x] = ocnt;
for( int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == f ) continue;
fa[v] = x;
dep[v] = dep[x] + 1;
dfs( v , x );
siz[x] += siz[v];
if( !zson[x] || siz[zson[x]] < siz[v] ) zson[x] = v;
}
oula[++ocnt] = x;fo2[x] = ocnt;
}
inline void dfs1( int x , int f ){
id[x] = ++ncnt;
top[x] = f;
if( !zson[x] ) return;
dfs1( zson[x] , f );
for( int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == fa[x] || v == zson[x] ) continue;
dfs1( v , v );
}
}
inline int LCA( int x , int y ){
while( top[x] != top[y] ){
if( dep[top[x]] < dep[top[y]] ) swap( x , y );
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
int main(){
scanf( "%d%d" , &n , &m );
for( int i = 1 ; i <= n ; i += 1 ){
scanf( "%d" , &b[i].x );b[i].num = i;
}
sort( b + 1 , b + n + 1 );
for( int i = 1 ; i <= n ; i ++ ){
if( b[i].x != b[i-1].x )
s[b[i].num] = ++bcnt;
else
s[b[i].num] = bcnt;
}
for( int i = 1 ; i < n ; i ++ ){
int x , y;scanf( "%d%d" , &x , &y );
if( !x || !y ) { root = max( x , y ); continue;}
G[x].push_back( y );
G[y].push_back( x );
}
root = 1;
dep[root] = 1;
dfs( root , 0 );
dfs1( root , root );
int num =0 ;
for( int i = 1 ; i <= m ; i ++ ){
int x , y , a , b;scanf( "%d%d" , &x , &y );
a = b = 0;
int lca = LCA( x , y );lc[i] = lca;
if( fo[x] > fo[y]) swap( x , y );
int l = fo2[x] , r = fo[y];
if( x == lca ){
l = fo[x] , r = fo[y];
}
ask[++num].l = l , ask[num].r = r , ask[num].a = a, ask[num].b = b , ask[num].num = i;
}
int len = sqrt( n * 2 );
for( int i = 1 ; i <= n * 2; i ++ )
belong[i] = ( i - 1 ) / len + 1;
sort( ask + 1 , ask + num + 1 );
int l = 0 , r = 0 , sum = 0;
for( int i = 1 ; i <= num ; i ++ ){
int L = ask[i].l , R = ask[i].r , a1 = ask[i].a , b1 = ask[i].b;
while( r < R ){
flag[oula[++r]] ^= 1;
if( flag[oula[r]] ){
cnt[s[oula[r]]] ++;
if( cnt[s[oula[r]]] == 1 ) sum ++;
}
else{
cnt[s[oula[r]]] --;
if( !cnt[s[oula[r]]] ) sum --;
}
}
while( r > R ){
flag[oula[r]] ^= 1;
if( !flag[oula[r]] ){
cnt[s[oula[r]]] --;
if( !cnt[s[oula[r]]] ) sum --;
}
else{
cnt[s[oula[r]]] ++;
if( cnt[s[oula[r]]] == 1 ) sum ++;
}
r --;
}
while( l < L ){
if( l ){
flag[oula[l]] ^= 1;
if( flag[oula[l]] ){
cnt[s[oula[l]]] ++;
if( cnt[s[oula[l]]] == 1 ) sum ++;
}
else{
cnt[s[oula[l]]] --;
if( !cnt[s[oula[l]]] ) sum --;
}
}
l ++;
}
while( l > L ){
flag[oula[--l]] ^= 1;
if( flag[oula[l]] ){
cnt[s[oula[l]]] ++;
if( cnt[s[oula[l]]] == 1 ) sum ++;
}
else{
cnt[s[oula[l]]] --;
if( !cnt[s[oula[l]]] ) sum --;
}
}
ans[ask[i].num] = sum;
if( !cnt[s[lc[ask[i].num]]] ) ans[ask[i].num] ++;
if( a1 && b1 ){
if( cnt[a1] && cnt[b1] && a1 != b1 ) ans[ask[i].num] --;
}
}
for( int i = 1 ; i <= m ; i ++ ){
printf( "%d\n" , ans[i] );
}
return 0;
}
树上带修莫队
=树上莫队+带修莫队
主要就是难写
例题:糖果公园(洛谷)
#pragma GCC optimize (2)
#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int MAXN = 200003;
int top[MAXN] , oula[MAXN] , fa[MAXN] , dep[MAXN] , ocnt , siz[MAXN] , zson[MAXN] , fo[MAXN] , lc[MAXN] , fo2[MAXN];
int anum , cnum , n , m , belong[MAXN] , cnt[MAXN] , flag[MAXN] , q , c[MAXN];
ll a[MAXN] , ans[MAXN] , w[MAXN];
struct node{
int num , l , r , tim;
friend bool operator < ( node a , node b ){
return (belong[a.l] ^ belong[b.l]) ? belong[a.l] < belong[b.l] : ((belong[a.r] ^ belong[b.r]) ? belong[a.r] < belong[b.r] : a.tim < b.tim);
}
}ask[MAXN];
struct edge{
int id , x;
}col[MAXN];
vector<int>G[MAXN];
inline void dfs( int x , int f ){
oula[++ocnt] = x;siz[x] = 1;fo[x] = ocnt;
for( int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == f ) continue;
fa[v] = x;
dep[v] = dep[x] + 1;
dfs( v , x );
siz[x] += siz[v];
if( !zson[x] || siz[zson[x]] < siz[v] ) zson[x] = v;
}
oula[++ocnt] = x;fo2[x] = ocnt;
}
inline void dfs1( int x , int f ){
top[x] = f;
if( !zson[x] ) return;
dfs1( zson[x] , f );
for( int i = 0 ; i < G[x].size() ; i ++ ){
int v = G[x][i];
if( v == fa[x] || v == zson[x] ) continue;
dfs1( v , v );
}
}
inline int LCA( int x , int y ){
while( top[x] != top[y] ){
if( dep[top[x]] < dep[top[y]] ) swap( x , y );
x = fa[top[x]];
}
return dep[x] < dep[y] ? x : y;
}
int main(){
//freopen( "2.in" , "r" , stdin );
//freopen( "2.out" , "w" , stdout) ;
scanf( "%d%d%d" , &n , &m , &q );
for( int i = 1 ; i <= m ; i += 1 )
scanf( "%lld" , &a[i] );
for( int i = 1 ; i <= n ; i ++ ) scanf( "%lld" , &w[i] );
for( int i = 1 ; i < n ; i ++ ){
int x , y;scanf( "%d%d" , &x , &y );
G[x].push_back( y );
G[y].push_back( x );
}
for( int i = 1 ; i <= n ; i ++ ) scanf( "%d" , &c[i] );
dep[1] = 1;
dfs( 1 , 0 );
dfs1( 1 , 1 );
for( int i = 1 ; i <= q ; i ++ ){
int op , x , y;
scanf( "%d%d%d" , &op , &x , &y );
if( op == 0 ){
col[++cnum].id = x , col[cnum].x = y;continue;
}
int lca = LCA( x , y );lc[++anum] = lca;
int l , r;
if( fo[x] > fo[y] ) swap( x , y );
if( x == lca ){
lc[anum] = 0;l = fo[x] , r = fo[y];
}
else{
l = fo2[x] , r = fo[y];
}
ask[anum].l = l , ask[anum].r = r ,ask[anum].tim = cnum , ask[anum].num = anum;
}
int len = pow( n * 2 , 2.0 / 3.0 );
for( int i = 1 ; i <= n * 2; i ++ )
belong[i] = ( i - 1 ) / len + 1;
sort( ask + 1 , ask + anum + 1 );
int l = 0 , r = 0 , now = 0;
ll sum = 0;
for( int i = 1 ; i <= anum ; i ++ ){
int L = ask[i].l , R = ask[i].r , T = ask[i].tim , num = ask[i].num;
while( R > r ){
flag[oula[++r]] ^= 1;
if( flag[oula[r]] ){
cnt[c[oula[r]]] ++;
sum += w[cnt[c[oula[r]]]] * a[c[oula[r]]];
}
else{
cnt[c[oula[r]]] --;
sum -= w[cnt[c[oula[r]]]+1] * a[c[oula[r]]];
}
}
while( R < r ){
flag[oula[r]] ^= 1;
if( flag[oula[r]] ){
cnt[c[oula[r]]] ++;
sum += w[cnt[c[oula[r]]]] * a[c[oula[r]]];
}
else{
cnt[c[oula[r]]] --;
sum -= w[cnt[c[oula[r]]]+1] * a[c[oula[r]]];
}
r --;
}
while( L > l ){
if( l ){
flag[oula[l]] ^= 1;
if( flag[oula[l]] ){
cnt[c[oula[l]]] ++;
sum += w[cnt[c[oula[l]]]] * a[c[oula[l]]];
}
else{
cnt[c[oula[l]]] --;
sum -= w[cnt[c[oula[l]]]+1] * a[c[oula[l]]];
}
}
l ++;
}
while( l > L ){
flag[oula[--l]] ^= 1;
if( flag[oula[l]] ){
cnt[c[oula[l]]] ++;
sum += w[cnt[c[oula[l]]]] * a[c[oula[l]]];
}
else{
cnt[c[oula[l]]] --;
sum -= w[cnt[c[oula[l]]]+1] * a[c[oula[l]]];
}
}
while( now < T ){
if( flag[col[++now].id] ){
++cnt[col[now].x];
sum += -w[cnt[c[col[now].id]]] * a[c[col[now].id]] + w[cnt[col[now].x]] * a[col[now].x];
cnt[c[col[now].id]]--;
}
swap( col[now].x , c[col[now].id] );
}
while( now > T ){
if( flag[col[now].id] ){
++cnt[col[now].x];
sum += -w[cnt[c[col[now].id]]] * a[c[col[now].id]] + w[cnt[col[now].x]] * a[col[now].x];
cnt[c[col[now].id]]--;
}
swap( col[now].x , c[col[now].id] );
now --;
}
ans[num] = sum;
if( lc[num] ) ans[num] += w[cnt[c[lc[num]]]+1] * a[c[lc[num]]];
}
for( int i = 1 ; i <= anum ; i ++ ){
printf( "%lld\n" , ans[i] );
}
return 0;
}
回滚莫队
回滚莫队针对的是如果已知[L,R],但是[L,R-1]与[L-1,R]不好求,即不好删除,但是可以支持撤销
所以询问的时候直接将左端点移动到当前块的最右端,然后就可以变成增加了,这样每次时间复杂度就会比普通莫队高一点
R还是从小到大增加。
例题:历史研究
问题描述
IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记。JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件。
日记中记录了连续N天发生的时间,大约每天发生一件。
事件有种类之分。第i天(1<=i<=N)发生的事件的种类用一个整数Xi表示,Xi越大,事件的规模就越大。
JOI教授决定用如下的方法分析这些日记:
- 选择日记中连续的一些天作为分析的时间段
- 事件种类t的重要度为t*(这段时间内重要度为t的事件数)
- 计算出所有事件种类的重要度,输出其中的最大值
现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。
输入格式
第一行两个空格分隔的整数N和Q,表示日记一共记录了N天,询问有Q次。
接下来一行N个空格分隔的整数X1...XN,Xi表示第i天发生的事件的种类
接下来Q行,第i行(1<=i<=Q)有两个空格分隔整数Ai和Bi,表示第i次询问的区间为[Ai,Bi]。
输出格式
输出Q行,第i行(1<=i<=Q)一个整数,表示第i次询问的最大重要度
样例输入 复制
5 5
9 8 7 8 9
1 2
3 4
4 4
1 4
2 4
样例输出 复制
9
8
8
16
16
#include<bits/stdc++.h>
using namespace std;
#define ll long long
char buf[1<<20],*p1,*p2;
#define GC (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?0:*p1++)
//#define GC getchar()
template<class T> inline void read(T &n){
char ch=GC;T w=1,x=0;
while(!isdigit(ch)){if(ch=='-') w=-1;ch=GC;}
while(isdigit(ch)){x=(x<<3)+(x<<1)+(ch^48);ch=GC;}
n=x*w;
}
const int MAXN = 5e5 + 3;
int n , q;
ll cnt[MAXN] , flag[MAXN];
ll ans[MAXN] , v[MAXN] , s[MAXN];
int belong[MAXN];
struct node{
int l , r , num;
friend bool operator < ( node a , node b ){
return ( belong[a.l] ^ belong[b.l] ) ? belong[a.l] < belong[b.l] : a.r < b.r;
}
}ask[MAXN];
struct edge{
ll x;
int num;
friend bool operator < ( edge a , edge b ){
return a.x < b.x;
}
}b[MAXN];
ll c[MAXN];
int main(){
scanf( "%d%d" , &n , &q );
for( int i = 1 ; i <= n ; i++ ){
scanf( "%lld" , &v[i]);s[i] = v[i];
b[i].x = v[i] , b[i].num = i;
}
sort( b + 1 , b + n + 1 );
int ncnt = 0;
for( int i = 1 ; i <= n ; i ++ ){
if( b[i].x != b[i-1].x )
c[b[i].num] = ++ncnt;
else
c[b[i].num] = ncnt;
}
for( int i = 1 ; i <= q ; i ++ ){
scanf( "%d%d" , &ask[i].l , &ask[i].r );
ask[i].num = i;
}
int len = sqrt( n );
for( int i = 1 ; i <= n ; i ++ ){
belong[i] = ( i - 1 ) / len + 1 ;
}
sort( ask + 1 , ask + q + 1 );
int l = 0 , r = 0 , last = 0;
ll ma = 0 , ma1 = 0 , lma = 0 , lma1 = 0;
for( int i = 1 ; i <= q ; i ++ ){
int L = ask[i].l , R = ask[i].r;
if( belong[L] == belong[R] ){
ll maxx = 0 , max1 = 0;
for( int j = L ; j <= R ; j ++ )
flag[c[j]] = 0;
for( int j = L ; j <= R ; j ++ ){
flag[c[j]] ++;
if( flag[c[j]] * v[j] > maxx ){
max1 = maxx;
maxx = flag[c[j]] * v[j];
}
else if( flag[c[j]] * v[j] > max1 )
max1 = flag[c[j]] * v[j];
}
ans[ask[i].num] = maxx;
continue;
}
if( belong[L] != last ){
memset( cnt , 0 , sizeof( cnt) ) ;
last = belong[L];
r = belong[L] * len;ma = ma1 = lma = lma1 = 0;
}
else
ma = lma , ma1 = lma1;
while( l <= belong[L] * len ){
flag[c[l++]] = 0;
}
while( l > L ){
flag[c[--l]] ++;
if( (flag[c[l]] + cnt[c[l]] ) * s[l] > ma ){
ma1 = ma;
ma = (flag[c[l]] + cnt[c[l]] ) * s[l];
}
else if( (flag[c[l]] + cnt[c[l]] ) * s[l] > ma1 )
ma1 = (flag[c[l]] + cnt[c[l]] ) * s[l];
}
while( r < R ){
cnt[c[++r]] ++;
if( (flag[c[r]] + cnt[c[r]] ) * s[r] > ma ){
ma1 = ma;
ma = (flag[c[r]] + cnt[c[r]] ) * s[r];
}
else if( (flag[c[r]] + cnt[c[r]] ) * s[r] > ma1 )
ma1 = (flag[c[r]] + cnt[c[r]] ) * s[r];
if( cnt[c[r]] * s[r] > lma ){
lma1 = lma;
lma = cnt[c[r]] * s[r];
}
else if( cnt[c[r]] * s[r] > lma1 ){
lma1 = cnt[c[r]] * s[r];
}
}
ans[ask[i].num] = ma;
}
for( int i = 1 ; i <= q ; i ++ )
printf( "%lld\n" , ans[i]);
return 0;
}