1. 程式人生 > >BZOJ4241 歷史研究 莫隊算法 堆

BZOJ4241 歷史研究 莫隊算法 堆

ref 博客 漏洞 成了 put sin 開始 鏈接 blog

歡迎訪問~原文出處——博客園-zhouzhendong&AK

去博客園看該題解

題目

Description

IOI國歷史研究的第一人——JOI教授,最近獲得了一份被認為是古代IOI國的住民寫下的日記。JOI教授為了通過這份日記來研究古代IOI國的生活,開始著手調查日記中記載的事件。

日記中記錄了連續N天發生的時間,大約每天發生一件。

事件有種類之分。第i(1<=i<=N)發生的事件的種類用一個整數Xi表示,Xi越大,事件的規模就越大。

JOI教授決定用如下的方法分析這些日記:

1. 選擇日記中連續的一些天作為分析的時間段

2. 事件種類t的重要度為t*(這段時間內重要度為

t的事件數)

3. 計算出所有事件種類的重要度,輸出其中的最大值

現在你被要求制作一個幫助教授分析的程序,每次給出分析的區間,你需要輸出重要度的最大值。

Input

第一行兩個空格分隔的整數NQ,表示日記一共記錄了N天,詢問有Q次。

接下來一行N個空格分隔的整數X1...XNXi表示第i天發生的事件的種類

接下來Q行,第i(1<=i<=Q)有兩個空格分隔整數AiBi,表示第i次詢問的區間為[Ai,Bi]

Output

輸出Q行,第i(1<=i<=Q)一個整數,表示第i次詢問的最大重要度

Sample Input

5 5

9 8 7 8 9

1 2

3 4

4 4

1 4

2 4

Sample Output

9

8

8

16

16

HINT

1<=N<=10^5

1<=Q<=10^5

1<=Xi<=10^9 (1<=i<=N)

Source

JOI 2013~2014 春季training合宿 競技1 By PoPoQQQ

題目概括

  給出一個序列,其中有n個數字。

  現在給出m個詢問,每次詢問格式為L R。我們設一個值在L~R範圍內的重要度為該值乘上該值在L~R範圍內出現過的次數。求L~R範圍內重要度最大的數值的重要度。

Sample Input

5 5

9 8 7 8 9

1 2

3 4

4 4

1 4

2 4

Sample Output

9

8

8

16

16

樣例解釋

對於詢問11~2範圍內,9出現了1次,重要度為98出現了1次,重要度為8;所以該區間內重要度最大為9

對於詢問23~4範圍內,7出現了1次,重要度為78出現了1次,重要度為8;所以該區間內重要度最大為8

對於詢問34~4範圍內,8出現了1次,重要度為8;所以該區間內重要度最大為8

對於詢問41~4範圍內,7出現了1次,重要度為78出現了2次,重要度為169出現了1次,重要度為9;所以該區間內重要度最大為8

對於詢問52~4範圍內,7出現了1次,重要度為78出現了2次,重要度為16;所以該區間內重要度最大為8

題解

  首先,無可置疑,離散化總要做的,閉著眼睛先做了。

  假設我們對於一個數值,設置其哈希值為棋離散化後的位置。

  然後思考,在線不會做,那麽離線;

  那麽就可以使用莫隊算法——不會,沒事,現學~ 鏈接1 鏈接2

  

  然後我還是來概括一下。

  

  我們把詢問按照一定的順序排列,然後大暴力修改邊界值(比如從Li~Li-1Ri~Ri-1這種段的修改),那麽時間復雜度為Σ(|Li-Li-1|+|Ri-Ri-1|) (1<i<=m)

  我們要使得這個值最小,可以采用分塊的方法。

  我們把整個區間(共n)分成每 sqrt(n) 一份,然後把詢問以 L 所在的塊的位置為第一關鍵字,把R作為第二關鍵字排序,然後就可以得到一個大約是 m sqrt(n) 的時間復雜度。

  

  不要把莫隊之後的處理看的很麻煩,其實就是大暴力!

  

  關鍵在於莫隊的排序。

  

  而本題要求的是最大值,所以要開一個堆來維護(要打線段樹也可以,我不攔你,有可能是被卡常~

  這個堆不簡單~

  

  它要支持尋找一個數值所對應的位置。

  

  一開始打成了一邊交換值,一邊交換位置的,實際上是有些漏洞的。

  

  對於我的算法,我的堆要保存3個量:

  1.堆中元素的值(在LR移動的時候,進入一個就在其哈希值的位置上加上其值,減掉的話就反一反)。

  2.堆中某一哈希值的元素所在位置。

  3.這樣還是不夠的,還要給每個元素配上一個它所對應的哈希值。

  至於具體怎麽維護~看代碼。

  這題我沒寫過線段樹,不知道怎樣。

  反正我的堆是跑了46.7秒,寫線段樹就自己估計一下吧……也許不是所有的線段樹都可以過的(要卡常?)……

  再在提醒一下,在改變區間範圍時候的大暴力時,對於LR分別要分兩種情況討論。

  具體還是看代碼唄~

代碼

#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
const int N=100000+5;
int n,m,bag_size;
int pos[N],hs,b[N],pv[N];
LL a[N],hash[N],v[N];
struct Query{
    int L,R,bh;
    LL ans;
}q[N];
bool cmp(Query a,Query b){
    int La=a.L/bag_size,Lb=b.L/bag_size;
    if (La==Lb)
        return a.R<b.R;
    return La<Lb;
}
bool cmpbh(Query a,Query b){
    return a.bh<b.bh;
}
int find(LL x){
    int le=1,ri=hs,mid;
    while (le<=ri){
        mid=(le+ri)>>1;
        if (hash[mid]==x)
            return mid;
        if (hash[mid]<x)
            le=mid+1;
        else
            ri=mid-1;
    }
}
void up_sift(int x){
    int i=x,j=i>>1;
    while (i>1&&v[i]>v[j]){
        pos[pv[i]]=j,pos[pv[j]]=i;
        swap(pv[i],pv[j]);
        swap(v[i],v[j]);
        i=j,j=i>>1;
    }
}
void down_sift(int x){
    int i=x,j=i<<1;
    while (j<=hs){
        if (j<hs&&v[j]<v[j+1])
            j++;
        if (v[i]>=v[j])
            break;
        pos[pv[i]]=j,pos[pv[j]]=i;
        swap(pv[i],pv[j]);
        swap(v[i],v[j]);
        i=j,j=i<<1;
    }
}
int main(){
//    freopen("mode.in","r",stdin);
//    freopen("mode.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1;i<=n;i++)
        scanf("%lld",&a[i]),hash[i]=a[i];
    sort(hash+1,hash+n+1);
    hs=1;
    for (int i=2;i<=n;i++)
        if (hash[i-1]!=hash[i])
            hash[++hs]=hash[i];
    for (int i=1;i<=n;i++)
        b[i]=find(a[i]);
    for (int i=1;i<=hs;i++)
        pos[i]=i,v[i]=0,pv[i]=i;
    for (int i=1;i<=m;i++){
        scanf("%d%d",&q[i].L,&q[i].R);
        q[i].bh=i;
    }
    bag_size=sqrt((double)n)+0.5;
    sort(q+1,q+m+1,cmp);
    int L=q[1].L,R=q[1].L-1;
    for (int i=1;i<=m;i++){
        while (R<q[i].R){
            R++;
            v[pos[b[R]]]+=a[R];
            up_sift(pos[b[R]]);
        }    
        while (R>q[i].R){
            v[pos[b[R]]]-=a[R];
            down_sift(pos[b[R]]);
            R--;
        }
        while (L<q[i].L){
            v[pos[b[L]]]-=a[L];
            down_sift(pos[b[L]]);
            L++;
        }
        while (L>q[i].L){
            L--;
            v[pos[b[L]]]+=a[L];
            up_sift(pos[b[L]]);
        }
        q[i].ans=v[1];
    }
    sort(q+1,q+m+1,cmpbh);
    for (int i=1;i<=m;i++)
        printf("%lld\n",q[i].ans);
    return 0;
}

BZOJ4241 歷史研究 莫隊算法 堆