faebdc的煩惱(二分查找+ST表實現RMQ)

faebdc的煩惱

二分查找+RMQ(ST表實現)

Luogu P1997
題目描述
給出一個升序排列的整數數組a1,a2,…an,你的任務是對於鳥哥的一系列詢問(i,j),回答ai,ai+1,…aj中出現次數最多的值所出現的次數。
輸入輸出格式
輸入格式:
輸入僅包含一組數據。
第一行爲兩個整數n,q(1<=n<=100000,1<=q<=200000)。第二行包含n個升序排列的整數a1,a2,…,an(-100000<=ai<=100000),代表每一道題的難度值。以下q行每行包含兩個整數i和j(1<=i<=j<=n),代表詢問的區間。
輸出格式:
對於每次詢問,單獨輸出一行,該行僅有一個整數,表示該區間內出現最多的數值所出現的次數。
輸入輸出樣例
輸入樣例#1:
9 1
1 1 1 2 2 3 3 4 4
3 8
輸出樣例#1:
2
說明
各個測試點1s

二分查找+RMQ(ST表實現)

題目簡化後如下:給定一個升序數列和多次詢問,求詢問區間內的衆數出現的次數。
看到這個題,我的第一想法就是序列的元素是多少不重要,重要的是每種元素的個數,所以我們就記錄左右端點和它的長度,長度另開一個數組來存,然後我們對詢問的區間內哪個數出現次數最多進行運算。
自然的,我們想求出左右端點的數字a[l],a[r]是第幾種數字,所以我們考慮二分。
而它們又並不一定在這個區間內全部出現,所以我們將二分出的種類序號對應的左(右)端點求出來,作一個差,把a[l]和a[r]在區間內出現的次數求出來,求一個最大值。
接下來我們就是把區間內全組完整存在的數出現的次數的最大值求出來,這裏我們就可以用RMQ來logn查詢區間最大值,然後在更新剛纔的最大值即可,這裏我的RMQ用了ST表來實現,當然也能用線段樹。
舉個例子吧:
如樣例所示
9 1
1 1 1 3 3 5 5 7 7
3 8
令p=3,q=8
由於1出現3次,3出現2次,5出現2次,7出現2次,所以個數數組爲:3 2 2 2,然後我們先初始化掉RMQ的ST表。
a[1].left=1,a[1].right=3
a[2].left=4,a[2].right=5
a[3].left=6,a[3].right=7
a[4].left=8,a[4].right=9
我們求區間【3,8】,所以首先二分求出位置3所在的組數,即爲第1組,同理位置8在第4組。
然後第1組在區間內有a[1].right-p+1=1個數;
然後第4組在區間內有a[4].left-q+1=1個數;
然後我們只需要求[p+1,q-1]組之間的出現次數的最大值,用ST表做一個RMQ問題就好了。
值得注意的一點就是,如果詢問區間是某一組所在區間的真子集,比如本例中詢問[1,2],那麼我們更新就應該更新a[1].right-a[1].left+1,而不是a[1].right-p+1與a[4].left-q+1。這一點一定注意,如果沒有的話是51分(第一次沒有AC就是這樣的)。
時間複雜度上表現不是很好:最差情況下是O(M*3logN)。
推介一波博客Stockholm_Sun萌萌的博客

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int i,j,m,n,t,temp;
int a[100001];
int map[100001][20];
struct data
{
    int ll,rr;
}b[100001];

int r()//讀入優化
{
    int ans=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
        f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        ans*=10;
        ans+=ch-'0';
        ch=getchar();
    }
    return ans*f;
}

int erfen1(int x)//兩個二分
{
    int mid,le=1,ri=temp,ans;
    while(le<=ri)
    {
        mid=le+ri;
        mid>>=1;
        if(b[mid].ll<x) ans=mid,le=mid+1;
        else if(b[mid].ll>x) ri=mid-1;
        else return mid;
    }
    return ans;
}

int erfen2(int x)
{
    int mid,le=1,ri=temp,ans;
    while(le<=ri)
    {
        mid=le+ri;
        mid>>=1;
        if(b[mid].rr<x) le=mid+1;
        else if(b[mid].rr>x) ans=mid,ri=mid-1;
        else return mid;
    }
    return ans;
}

void work()//ST表的構建
{
    int i,j;
    for(j=1;1<<j<=n;j++)
    for(i=1;i+(1<<j)-1<=n;i++)
    map[i][j]=max(map[i][j-1],map[i+(1<<j-1)][j-1]);
}

int question(int z,int y)//區間查詢最大值
{
    int x=(int)(log(y-z+1)/log(2));
    return max(map[z][x],map[y-(1<<x)+1][x]);
}

int main()
{
    freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
    n=r(),m=r();
    b[++temp].ll=1;
    a[1]=r();
    for(i=2;i<=n;i++)
    {
        a[i]=r();
        if(a[i]!=a[i-1])
        {
            b[temp].rr=i-1;
            b[++temp].ll=i;
        }
    }
    b[temp].rr=n;

    for(i=1;i<=temp;i++)
    {
        map[i][0]=b[i].rr-b[i].ll+1;//初始化ST表
    }
    work();
    int p,q,maxx=0;
    for(i=1;i<=m;i++)
    {
        maxx=0;
        p=r(),q=r();
        int st=erfen1(p);//這兩種二分一定要區分開
        int en=erfen2(q);
        maxx=max(maxx,min(b[st].rr-p+1,q-p+1));//特殊情況判斷
        maxx=max(maxx,min(q-b[en].ll+1,q-p+1));
        if(st+1<=en-1)
        maxx=max(maxx,question(st+1,en-1));
        cout<<maxx<<endl;
    }
    return 0;
}
/*

*/

這裏寫圖片描述

發佈了131 篇原創文章 · 獲贊 102 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章