1. 程式人生 > >BZOJ2724 蒲公英 【分塊】

BZOJ2724 蒲公英 【分塊】

BZOJ2724 蒲公英

題目背景

親愛的哥哥:
你在那個城市裡面過得好嗎?
我在家裡面最近很開心呢。昨天晚上奶奶給我講了那個叫「絕望」的大壞蛋的故事的說!它把人們的房子和田地搞壞,還有好多小朋友也被它殺掉了。我覺得把那麼可怕的怪物召喚出來的那個壞蛋也很壞呢。不過奶奶說他是很難受的時候才做出這樣的事的……
最近村子裡長出了一大片一大片的蒲公英。一颳風,這些蒲公英就能飄到好遠的地方了呢。我覺得要是它們能飄到那個城市裡面,讓哥哥看看就好了呢!
哥哥你要快點回來哦!
愛你的妹妹 Violet
Azure 讀完這封信之後微笑了一下。
“蒲公英嗎……”

題目描述

在鄉下的小路旁種著許多蒲公英,而我們的問題正是與這些蒲公英有關。
為了簡化起見,我們把所有的蒲公英看成一個長度為n的序列

(a1,a2..an)其中 ai為一個正整數,表示第i棵蒲公英的種類編號。
而每次詢問一個區間 [l,r],你需要回答區間裡出現次數最多的是哪種蒲公英,如果有若干種蒲公英出現次數相同,則輸出種類編號最小的那個。
注意,你的演算法必須是線上的

輸入輸出格式

輸入格式:

第一行兩個整數 n,m ,表示有n株蒲公英,m 次詢問。
接下來一行n個空格分隔的整數 ai ,表示蒲公英的種類
再接下來m 行每行兩個整數 l0,r0 ,我們令上次詢問的結果為 x(如果這是第一次詢問, 則 x=0)。

l=(l0+x1)modn+1,r=(r0+x1)modn+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% 的資料,保證 1n,m30001n,m3000
對於 100% 的資料,保證 1n40000,1m50000,1ai109

區間求眾數

分塊

cnt[i][j]表示第i個數在前j個塊中的數量
ans[i][j]表示第i到第j個塊中的眾數
先離散,然後預處理cntans,對於查詢,先把答案設定為完整塊的眾數,不是完整塊的部分就暴力列舉進行比較好了

時間&空間效率O(nsqrt(n))

#include<bits/stdc++.h>
using namespace std;
#define N 210
#define M 40010
#define For(x,a,b) for(int x=a;x<=b;x++)
map<int,int> mp;
int n,m,tot=0,lastans=0;
int a[M],b[M],pre[M],t[N]={0};
int block[M],L[N],R[N];
int cnt[M][N]={0},ans[N][N]={0};
int calc(int id,int lb,int rb){
    return cnt[id][rb]-cnt[id][lb-1];
}
void init(){
    int siz=sqrt(n);
    For(i,1,n)block[i]=(i-1)/siz+1;
    int bsiz=block[n];
    For(i,1,bsiz)L[i]=R[i-1]+1,R[i]=min(n,i*siz);
    For(i,1,n)cnt[b[i]][block[i]]++;
    For(i,1,tot)For(j,1,bsiz)cnt[i][j]+=cnt[i][j-1];
    For(i,1,bsiz)
        For(j,i,bsiz){
            int tmp=ans[i][j-1];
            For(k,L[j],R[j]){
                int t1=calc(b[k],i,j),t2=calc(tmp,i,j);
                if((t1==t2&&pre[b[k]]<pre[tmp])||t1>t2)tmp=b[k];
            }
            ans[i][j]=tmp;
        }
}
bool check(int id,int tmp){
    if(t[id]>t[tmp])return 1;
    if(t[id]==t[tmp]&&pre[id]<pre[tmp])return 1;
    return 0;
}
int solve(int l,int r){
    int pl=block[n]+1,pr=0;
    For(i,1,block[n])if(L[i]>=l)pl=min(pl,i);
    For(i,1,block[n])if(R[i]<=r)pr=max(pr,i);
    if(pl>pr){ 
        int tmp=0;
        For(i,l,r)t[b[i]]=0;
        For(i,l,r)t[b[i]]++;
        For(i,l,r)if(check(b[i],tmp))tmp=b[i];
        return tmp;
    }
    int tmp=ans[pl][pr];
    t[tmp]=calc(tmp,pl,pr);
    For(i,l,L[pl]-1)t[b[i]]=calc(b[i],pl,pr);
    For(i,R[pr]+1,r)t[b[i]]=calc(b[i],pl,pr);
    For(i,l,L[pl]-1)t[b[i]]++;
    For(i,R[pr]+1,r)t[b[i]]++;
    For(i,l,L[pl]-1)if(check(b[i],tmp))tmp=b[i];
    For(i,R[pr]+1,r)if(check(b[i],tmp))tmp=b[i];
    return tmp;
}
int main(){
    scanf("%d%d",&n,&m);
    For(i,1,n){
        scanf("%d",&a[i]);
        if(!mp.count(a[i])){
            mp[a[i]]=++tot;
            pre[tot]=a[i];
        }
        b[i]=mp[a[i]];
    }
    init();
    For(i,1,m){
        int l,r;scanf("%d%d",&l,&r);
        l=(l+lastans-1)%n+1;
        r=(r+lastans-1)%n+1;
        if(l>r)swap(l,r);
        lastans=pre[solve(l,r)];
        printf("%d\n",lastans);
    }
    return 0;
}