萌新求助,关于回滚莫队排序
查看原帖
萌新求助,关于回滚莫队排序
486863
Horb7楼主2022/4/2 23:38

为什么下面的排序方法不对呢,普通莫队和带修莫队按照下面那种规则就是对的呢qwq?

struct query {
    int l, r, id;

    bool operator<(const query &rhs) const {
        if (bel[l] != bel[rhs.l]) return l < rhs.l;
        return r < rhs.r;
//        if (l / len != rhs.l / len) return l < rhs.l;
//        if (l / len & 1) return r < rhs.r;
//        else return r > rhs.r;
    }
} Q[N];

完整代码:

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int N = 100010;
int n, q, a[N], b[N]; // b存储离散化后的数组
vector<int> alls;

int bel[N], L[N], R[N], len, sq;

int cnt[N], __cnt[N]; // __cnt用来暴力求块内的查询
ll ans[N], Ans; // Ans是当前区间的答案

struct query {
    int l, r, id;

    bool operator<(const query &rhs) const {
        if (bel[l] != bel[rhs.l]) return l < rhs.l;
        return r < rhs.r;
//        if (l / len != rhs.l / len) return l < rhs.l;
//        if (l / len & 1) return r < rhs.r;
//        else return r > rhs.r;
    }
} Q[N];

void init ()
{
    sq = sqrt(n);
    len = n / sq;
    for (int i = 1; i <= sq; i ++ ) {
        L[i] = (i - 1) * len + 1;
        R[i] = i * len;
    }
    if (R[sq] < n) {
        sq ++ ;
        L[sq] = R[sq-1] + 1;
        R[sq] = n;
    }
    // init bel
    for (int i = 1; i <= sq; i ++ )
        for (int j = L[i]; j <= R[i]; j ++ )
            bel[j] = i;
}

void del (int x) {
    -- cnt[x];
}

void add (int x, ll &nowAns) {
    ++ cnt[x];
    nowAns = max(nowAns, 1ll * cnt[x] * alls[x]);
}

int main ()
{
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i ++ ) {
        scanf("%d", &a[i]);
        alls.push_back(a[i]);
    }
    for (int i = 1; i <= q; i ++ ) {
        int l, r; scanf("%d%d", &l, &r);
        Q[i] = { l, r, i };
    }

    // 离散化
    sort(alls.begin(), alls.end());
    alls.erase(unique(alls.begin(), alls.end()), alls.end());
    for (int i = 1; i <= n; i ++ ) {
        b[i] = lower_bound(alls.begin(), alls.end(), a[i]) - alls.begin();
    }

    init();
    sort(Q + 1, Q + q + 1);

    int l = 1, r = 0, last_block = 0;
    for (int i = 1; i <= q; i ++ ) {
        // 如果询问的左右端点在一个块内,O(√m)解决
        if (bel[Q[i].l] == bel[Q[i].r]) {
            for (int j = Q[i].l; j <= Q[i].r; j ++ ) ++ __cnt[b[j]];
            for (int j = Q[i].l; j <= Q[i].r; j ++ ) {
                ans[Q[i].id] = max(ans[Q[i].id], 1ll * __cnt[b[j]] * alls[b[j]]);
            }
            for (int j = Q[i].l; j <= Q[i].r; j ++ ) -- __cnt[b[j]];
            continue;
        }

        // 如果上一个块和左端点的块不同,重新设置l和r
        // l = R + 1, r = R
        // 因为这里需要的是只增不缩的回滚莫队,所以要把l设置为最大值,然后往外拓展
        if (last_block != bel[Q[i].l]) {
            while(l < R[bel[Q[i].l]] + 1) del(b[l ++ ]);
            while(r > R[bel[Q[i].l]]) del(b[r -- ]);
            Ans = 0;
            last_block = bel[Q[i].l];
        }

        // 直接修改右区间
        while(r < Q[i].r) add(b[++ r], Ans);

        // 注意修改作左区间需要回滚,不能真正修改,我们用一个tmp来维护答案
        int __l = l;
        ll tmp = Ans;

        // 使用临时变量 __l 和 tmp 来更新
        while(__l > Q[i].l) add(b[-- __l], tmp);

        ans[Q[i].id] = tmp;

        // 使用 __l 和 l 来实现回滚操作
        while(__l < l) del(b[__l ++ ]);
    }

    for (int i = 1; i <= q; i ++ ) {
        printf("%lld\n", ans[i]);
    }
    return 0;
}
2022/4/2 23:38
加载中...