BZOJ4241: 歷史研究(回滾莫隊)

自為風月馬前卒發表於2018-09-14

題意

給出$n$個數,每次詢問區間$[L, R]$內 每個數*出現次數 的最大值

Sol

回滾莫隊,名字真萌qwq

考慮如果用正常莫隊的話我們是無法刪除的,因為一旦刪除了最大元素就無法找到次大元素

這時候有人提出了一種新的計算方式

思想很簡單:對於每個詢問按照左端點的塊的編號進行排序,相同的話按又端點排序

然後暴力計算每個塊。

如果詢問的兩個端點在同一個塊中,直接暴力計算,時間複雜度$O(sqrt{n})$

如果不在同一個塊中,這時候右端點是不斷遞增的,因此暴力計算右端點的複雜度為$O(n)$

但是左端點的位置在塊內可是飄忽不定的啊qwq

簡單,每次詢問之後把左端點移動到所在塊的最右段即可,每次計算左端點的複雜度為$O(sqrt{n})$

因為有$sqrt{n}$個塊,因此總的時間複雜度為$O(sqrt{n}n)$

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define LL long long 
using namespace std;
const int MAXN = 1e5 + 10, INF = 1e9 + 7;
inline int read() {
    char c = getchar(); int x = 0, f = 1;
    while(c < `0` || c > `9`) {if(c == `-`) f = -1; c = getchar();}
    while(c >= `0` && c <= `9`) x = x * 10 + c - `0`, c = getchar();
    return x * f;
}
int N, Q;
LL out[MAXN], ans;
int be[MAXN], date[MAXN], cnt[MAXN], a[MAXN], tot, base, num;
struct Query {
    int l, r, id;
    bool operator < (const Query &rhs) const {
        return be[l] == be[rhs.l] ? r < rhs.r : be[l] < be[rhs.l];
    }
}q[MAXN];
LL solve(int l, int r) {
    static int tim[MAXN]; LL ans = 0;
    for(int i = l; i <= r; i++) tim[a[i]] = 0;
    for(int i = l; i <= r; i++) tim[a[i]]++, ans = max(ans, 1ll * tim[a[i]] * date[a[i]]);
    return ans;
}
void Add(int x) {
    cnt[a[x]]++;
    ans = max(ans, 1ll * cnt[a[x]] * date[a[x]]);
}
void Del(int x) {
    cnt[a[x]]--;
}
int Get(int i, int id) {
    int R = min(N, id * base), ql = R + 1, qr = ql - 1; ans = 0;
    memset(cnt, 0, sizeof(cnt));
    for(; be[q[i].l] == id; i++) {
        if(be[q[i].l] == be[q[i].r]) {out[q[i].id] = solve(q[i].l, q[i].r); continue;}
        while(qr < q[i].r) Add(++qr);
        LL cur = ans;
        while(ql > q[i].l) Add(--ql);
        out[q[i].id] = ans;
        while(ql < R + 1) Del(ql++);//每次詢問完之後重新統計答案
        ans = cur;
    }
    return i;
}
main() {
    //freopen("4241.in", "r", stdin);
    //freopen("4241.out", "w", stdout);
    N = read(); Q = read(); base = sqrt(N);
    for(int i = 1; i <= N; i++) {
        a[i] = date[i] = read(); be[i] = (i - 1) / base + 1;
        num = max(num, be[i]);
    }

    sort(date + 1, date + N + 1);
    int tot = unique(date + 1, date + N + 1) - date - 1;
    for(int i = 1; i <= N; i++) a[i] = lower_bound(date + 1, date + N + 1, a[i]) - date;

    for(int i = 1; i <= Q; i++) q[i].l = read(), q[i].r = read(), q[i].id = i;
    sort(q + 1, q + Q + 1);

    for(int i = 1, id = 1; id <= num; id++) 
        i = Get(i, id);
    

    for(int i = 1; i <= Q; i++)
        printf("%lld
", out[i]);
    return 0;
}
/*
2
3 2
1 2 3
2 2
1 3
3 2
1 2 3
2 2
1 3

*/

相關文章