1. 程式人生 > >Luogu P4168 [Violet]蒲公英

Luogu P4168 [Violet]蒲公英

P4168 [Violet]蒲公英

題意

題目背景

親愛的哥哥:

你在那個城市裡面過得好嗎?

我在家裡面最近很開心呢。昨天晚上奶奶給我講了那個叫「絕望」的大壞蛋的故事的說!它把人們的房子和田地搞壞,還有好多小朋友也被它殺掉了。我覺得把那麼可怕的怪物召喚出來的那個壞蛋也很壞呢。不過奶奶說他是很難受的時候才做出這樣的事的……

最近村子裡長出了一大片一大片的蒲公英。一颳風,這些蒲公英就能飄到好遠的地方了呢。我覺得要是它們能飄到那個城市裡面,讓哥哥看看就好了呢!

哥哥你要快點回來哦!

愛你的妹妹 \(Violet\)

\(Azure\) 讀完這封信之後微笑了一下。

“蒲公英嗎……”

題目描述

在鄉下的小路旁種著許多蒲公英,而我們的問題正是與這些蒲公英有關。

為了簡化起見,我們把所有的蒲公英看成一個長度為\(n\)的序列\((a_1,a_2\cdots a_n)\),其中\(a_i\)為一個正整數,表示第\(i\)棵蒲公英的種類編號。

而每次詢問一個區間\([l,r]\),你需要回答區間裡出現次數最多的是哪種蒲公英,如果有若干種蒲公英出現次數相同,則輸出種類編號最小的那個。

注意,你的演算法必須是線上的

輸入輸出格式

輸入格式:

第一行兩個整數\(n,m\),表示有\(n\)株蒲公英,\(m\)次詢問。

接下來一行\(n\)個空格分隔的整數\(a_i\)

,表示蒲公英的種類

再接下來\(m\)行每行兩個整數\(l_0,r_0\),我們令上次詢問的結果為\(x\)(如果這是第一次詢問,則\(x=0\))。

\(l=(l_0+x-1)\bmod n + 1,r=(r_0+x-1)\bmod n + 1\),如果\(l>r\),則交換\(l,r\)

最終的詢問區間為[l,r]。

輸出格式:

輸出\(m\)行。每行一個整數,表示每次詢問的結果。

輸入輸出樣例

輸入樣例#1:

6 3
1 2 3 2 1 2
1 5
3 6
1 5

輸出樣例#1:

1
2
1

說明

對於\(20\%\)的資料,保證\(1\le n,m\le 3000\)

對於\(100\%\)的資料,保證\(1\le n\le 40000,1\le m\le 50000,1\le a_i\le 10^9\)

思路

\(600\ AC\)!整理部落格。 --Uranus

重新拾起分塊這個毒瘤東西,並在\(alecli\)大佬的推薦下做了這道題。

我們把序列分成塊,離散化之後記錄每個數字在各個塊內出現的次數,那麼出現最多的就是眾數了。這裡我用陣列\(s[i][j][k]\)表示第\(i\)塊到第\(j\)塊數字\(k\)的出現次數。然後我們統計區間眾數,用\(md[i][j]\)記錄眾數大小,\(tms[i][j]\)記錄眾數出現次數。

預處理到這裡就結束了,接下來考慮查詢操作。對於每次查詢的區間,一定是由一個大塊和這個大塊左邊的一些數,以及右邊的一些陣列成的,那麼顯然,這個區間最後的眾數為大塊的眾數,或者大塊左邊出現的數,或者大塊右邊出現的數。

那麼我們就直接用之前統計出的大塊的三個陣列,在這個基礎上把左右兩小塊用預處理時的相同方法加進來,統計答案後再把小塊去掉,就可以很方便的查詢並保持預處理陣列了。

既然是分塊,那麼細節一定是很多的,具體來說有這幾條:

  • 注意離散化,注意常數優化。
  • 如果查詢區間不包含大塊,直接把兩個小塊暴力處理。
  • \(alecli\)說分塊的大小會影響時間複雜度,並告訴我塊的大小最好為\(n^\frac{2}{3}\),雖然並不知道為什麼。

只要耐心除錯,最終一定能\(AC\)的,祝你好運!

AC程式碼

#include<bits/stdc++.h>
using namespace std;
const int MAXN=4e4+5,MAXM=45;
int n,m,len,ans,cnt,a[MAXN],b[MAXN],c[MAXN];
int sz,num,l[MAXM],r[MAXM],belong[MAXN],s[MAXM][MAXM][MAXN],md[MAXM][MAXM],tms[MAXM][MAXM];
int read()
{
    int re=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) re=(re<<3)+(re<<1)+ch-'0',ch=getchar();
    return re;
}
void print(int x,bool first)
{
    if(!x)
    {
        if(first) putchar('0');
        return ;
    }
    print(x/10,false);
    putchar('0'+x%10);
}
void prework()
{
    num=pow(double(n),1.0/3.0),sz=n/num;
    for(int i=1;i<=num;i++) l[i]=r[i-1]+1,r[i]=sz*i;
    if(r[num]<n) num++,l[num]=r[num-1]+1,r[num]=n;
    for(int i=1;i<=num;i++)
        for(int j=l[i];j<=r[i];j++)
            belong[j]=i;
    sort(b+1,b+n+1);
    len=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;i++) c[i]=lower_bound(b+1,b+len+1,a[i])-b;
    for(int i=1;i<=num;i++)
        for(int j=i;j<=num;j++)
        {
            for(int k=l[i];k<=r[j];k++) s[i][j][c[k]]++;
            for(int k=1;k<=len;k++)
                if(tms[i][j]<s[i][j][k]||(tms[i][j]==s[i][j][k]&&k<md[i][j]))
                    md[i][j]=k,tms[i][j]=s[i][j][k];
        }
}
void ask(int ll,int rr)
{
    if(ll>rr) swap(ll,rr);
    int lbe=belong[ll],rbe=belong[rr],L,R;ans=cnt=0;
    if(rbe-lbe<=2) L=R=0;
    else L=lbe+1,R=rbe-1;
    if(L==R)
    {
        for(int i=ll;i<=rr;i++)
        {
            s[L][R][c[i]]++;
            if(cnt<s[L][R][c[i]]||(cnt==s[L][R][c[i]]&&ans>c[i]))
                cnt=s[L][R][c[i]],ans=c[i];
        }
        for(int i=ll;i<=rr;i++) s[L][R][c[i]]--;
    }
    else
    {
        ans=md[L][R],cnt=tms[L][R];
        for(int i=ll;i<=r[lbe];i++)
        {
            s[L][R][c[i]]++;
            if(cnt<s[L][R][c[i]]||(cnt==s[L][R][c[i]]&&ans>c[i]))
                cnt=s[L][R][c[i]],ans=c[i];
        }
        for(int i=l[rbe];i<=rr;i++)
        {
            s[L][R][c[i]]++;
            if(cnt<s[L][R][c[i]]||(cnt==s[L][R][c[i]]&&ans>c[i]))
                cnt=s[L][R][c[i]],ans=c[i];
        }
        for(int i=ll;i<=r[lbe];i++) s[L][R][c[i]]--;
        for(int i=l[rbe];i<=rr;i++) s[L][R][c[i]]--;
    }
    ans=b[ans];
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++) a[i]=b[i]=read();
    prework();
    while(m--)
    {
        int x=(read()+ans-1)%n+1,y=(read()+ans-1)%n+1;
        ask(x,y);
        print(ans,true);putchar('\n');
    }
    return 0;
}