題解【luogu4168 [Violet]蒲公英】


Description

給出一個長度為 \(n\) 序列 \(a\)\(m\) 次詢問,每次詢問區間 \([l,r]\) 里的眾數(出現次數最多的數)。若有多個,輸出最小的。

\(a_i \leq 10^9, n \leq 40000, m \leq 50000\),強制在線。

Solution

\(a_i \leq 10^9\) ,先離散化。然后

算法一:暴力 \(n ^ 2\) ,預計得分 20 ; 實際得分 20 (開了 O2 直接變成 85 啥操作)

算法二:\(n \leq 40000\) , 看來需要搬出分塊大法。

預處理出兩個數組:

\(p[i][j]\):表示第 \(i\) 個塊 到第 \(j\) 個塊的(最小的)眾數。

\(s[i][j]\):類似於前綴和,在前 \(i\) 個(包括 \(i\) )個塊中 \(j\) (離散化之后的值)出現了幾次。

如何預處理 \(p,s\)

對於 \(s\) ,直接每個塊掃一遍,復雜度 \(O(n \sqrt n)\)

對於 \(p\) ,雙重循環枚舉 \(i,j\),開一個數組暴力統計每個數出現了多少次。復雜度 \(O(\sqrt n \sqrt n \sqrt n)=O(n \sqrt n)\)

預處理 \(p,s\) 有啥用呢?對於一個詢問 \([l,r]\) ,設 \(l\) 在第 \(posl\) 個塊中,\(r\) 在第 \(posr\) 個塊中。那么分兩種情況:

第一種:\(posr - posl <= 1\),直接暴力掃 \(l,r\),復雜度 \(O(\sqrt n)\)

第二種:\(posr - posl >= 2\),如下圖:

紅線就是 \(l\),藍線就是 \(r\),黑線是塊與塊的分割線。

答案 \(\in\) \(\{\text{黃線中的元素}\} \cup \{\text{綠線的眾數}\}\)

綠線的眾數在之前已經預處理好了,對於黃線中的每一個元素在區間\([l,r]\)中出現的次數就是 在黃線中出現的次數 + 在綠線中出現的次數。

對於在黃線中出現的次數,可以直接掃,復雜度 \(O(\sqrt n)\)

對於在綠線中出現的次數,可以根據之前處理的前綴和算出。

這樣每個元素就可以在 \(O(\sqrt n)\) 的時間內求出出現次數,然后就可以愉快的AC神仙分塊黑題了了。 (細節很多,調了很久)

Code

#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 40040;
const int K = 220;
int n, m, L, len, sum[K][N], vis[N];
int tmpnum[N], B[N], last, pre[N];
struct getin {
    int id, d, se;
}a[N];
struct node {
    int num, s;
}p[K][K];
inline bool cmp1(getin x, getin y) { return x.d < y.d; }
inline bool cmp2(getin x, getin y) { return x.id < y.id; }
inline int getB(int x) {
    int ret = x / L;
    if(x % L) ret++;
    return ret;
}
inline void prework() {
    for(int i = 1; i <= len; i++) {
        memset(B, 0, sizeof(B)); node tmp;
        tmp.num = tmp.s = 0;
        for(int j = i; j <= len; j++) {
            for(int k = (j - 1) * L + 1; k <= min(n, j * L); k++) {
                B[a[k].se]++;
                if(B[a[k].se] > tmp.s) {
                    tmp.num = a[k].se;
                    tmp.s = B[a[k].se];
                }
                else if(B[a[k].se] == tmp.s)
                    tmp.num = min(tmp.num, a[k].se), 
                    tmp.s = B[a[k].se];
            }
            p[i][j] = tmp;
        }
    }
    for(int i = 1; i <= len; i++) {
        for(int j = 1; j <= n; j++) sum[i][a[j].se] = sum[i - 1][a[j].se];
        for(int j = (i - 1) * L + 1; j <= min(n, i * L); j++) 
            sum[i][a[j].se]++;
    }
}
int main() {
    scanf("%d%d", &n, &m); L = sqrt(n);
    len = (n + L - 1) / L;
    for(int i = 1; i <= n; i++)
        scanf("%d", &a[i].d), a[i].id = i;
    sort(a + 1, a + n + 1, cmp1); a[0].d = -1;
    for(int i = 1; i <= n; i++) {
        a[i].se = a[i - 1].se;
        if(a[i - 1].d != a[i].d) 
            a[i].se++;
        pre[a[i].se] = a[i].d;
    }
    sort(a + 1, a + n + 1, cmp2);
    prework(); 
    for(int i = 1; i <= m; i++) {
        int l, r; scanf("%d%d", &l, &r);
        l = (l + last - 1) % n + 1;
        r = (r + last - 1) % n + 1;
        if(l > r) swap(l, r);
        int posl = getB(l), posr = getB(r);
         if(posr - posl <= 2) {
            int ans = 0;
            for(int j = l; j <= r; j++) tmpnum[a[j].se] = 0;
            for(int j = l; j <= r; j++) {
                tmpnum[a[j].se]++;
                if(tmpnum[a[j].se] > tmpnum[ans]) ans = a[j].se;
                else if(tmpnum[a[j].se] == tmpnum[ans]) ans = min(ans, a[j].se);
            }
            printf("%d\n", last = pre[ans]);
        } 
        else {
            int ans = p[posl + 1][posr - 1].num;
            tmpnum[ans] = 0, vis[ans] = 0;
            for(int j = l; j <= min(n, posl * L); j++) tmpnum[a[j].se] = 0, vis[a[j].se] = 0;
            for(int j = (posr - 1) * L + 1; j <= r; j++) tmpnum[a[j].se] = 0, vis[a[j].se] = 0;
            for(int j = l; j <= min(n, posl * L); j++) tmpnum[a[j].se]++;
            for(int j = (posr - 1) * L + 1; j <= r; j++) tmpnum[a[j].se]++;
            int MXnum, MX = 0;
            for(int j = l; j <= min(n, posl * L); j++)
                if(!vis[a[j].se]) {
                    vis[a[j].se] = 1;
                    int val = tmpnum[a[j].se] + sum[posr - 1][a[j].se] - sum[posl][a[j].se];
                    if(MX < val)
                        MX = val, 
                        MXnum = a[j].se;
                    else if(MX == val) MXnum = min(MXnum, a[j].se);
                }
            for(int j = (posr - 1) * L + 1; j <= r; j++)
                if(!vis[a[j].se]) {
                    vis[a[j].se] = 1;
                    int val = tmpnum[a[j].se] + sum[posr - 1][a[j].se] - sum[posl][a[j].se];
                    if(MX < val)
                        MX = val, 
                        MXnum = a[j].se;
                    else if(MX == val) MXnum = min(MXnum, a[j].se);
                }
            if(MX > tmpnum[ans] + p[posl + 1][posr - 1].s) ans = MXnum;
            else if(MX == tmpnum[ans] + p[posl + 1][posr - 1].s) ans = min(ans, MXnum);
            printf("%d\n", last = pre[ans]);
        } 
    }
    return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM