莫队(总结)

前置知识

分块。

普通莫队

首先来看一道例题:

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\sqrt{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数颜色
时间限制 : - MS   空间限制 : - KB 
评测说明 : 1s,256m

问题描述

墨墨购买了一套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教授决定用如下的方法分析这些日记:

  1. 选择日记中连续的一些天作为分析的时间段
  2. 事件种类t的重要度为t*(这段时间内重要度为t的事件数)
  3. 计算出所有事件种类的重要度,输出其中的最大值
    现在你被要求制作一个帮助教授分析的程序,每次给出分析的区间,你需要输出重要度的最大值。

输入格式

第一行两个空格分隔的整数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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值