bzoj4241 历史研究 分块

时间:2019-10-16
本文章向大家介绍bzoj4241 历史研究 分块,主要包括bzoj4241 历史研究 分块使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4241

题解

这道题要求出来的就是一个区间带权众数。

回忆一下一般的区间众数怎么求:

分块,对于每一次询问 \(l, r\),如果 \(l, r\) 不在同一个整块中,那么答案一定是中间的整块的众数,或者两旁的剩下来的数。

这个东西可以预处理一个 \(f[i][j]\) 表示从第 \(i\) 块到 \(j\) 的众数就可以单次 \(\sqrt n\) 求出了。

对于两旁的剩数,每个数可以预处理每个值的出现位置的序列,在上面二分求出每个数的出现次数。


可以发现上面的结论依然可以用于带权众数。

不过这道题的数据范围大了一点,有 \(10^5\),所以需要 \(O(1)\) 求出一个数在某一整段中的出现次数。因为是整段,也就是开头到结尾包含的都是整块,所以可以预处理一个前缀和 \(s[i][j]\) 表示前 \(i\) 块中 \(j\) 的出现次数就可以求出来了。


伤心,\(O((n+q)\sqrt n)\)\(O((n+q)\sqrt n \log n)\) 吊打。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
    int f = 0, c;
    while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
    x = c & 15;
    while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
    f ? x = -x : 0;
}

const int N = 1e5 + 7;
const int B = 316 + 7;

#define bl(x) (((x) - 1) / blo + 1)
#define st(x) (((x) - 1) * blo + 1)
#define ed(x) std::min((x) * blo, n)

int n, dis, Q, blo;
int a[N], b[N], s[B][N], cnt[N];
ll f[B][N];

inline void ycl() {
    for (int i = 1; i <= bl(n); ++i)
        for (int j = st(i); j <= ed(i); ++j) ++s[i][a[j]];
    for (int i = 1; i <= bl(n); ++i)
        for (int j = 1; j <= dis; ++j) s[i][j] += s[i - 1][j];
    for (int i = 1; i <= bl(n); ++i) {
        memset(cnt, 0, sizeof(int) * (dis + 1));
        int x = st(i);
        ll mx = 0;
        for (int j = x; j <= n; ++j) {
            smax(mx, (ll)b[a[j]] * ++cnt[a[j]]);
            f[i][j] = mx;
        }
    }
    memset(cnt, 0, sizeof(int) * (dis + 1));
}

inline ll qry(int l, int r) {
    ll ans = 0;
    if (bl(l) == bl(r)) {
        for (int i = l; i <= r; ++i) smax(ans, (ll)b[a[i]] * ++cnt[a[i]]);
        for (int i = l; i <= r; ++i) cnt[a[i]] = 0;
        return ans;
    }
    ans = f[bl(l) + 1][r];
    int sl = bl(l) + 1, sr = bl(r) - 1;
    for (int i = l; i <= ed(bl(l)); ++i) smax(ans, (ll)b[a[i]] * (++cnt[a[i]] + s[sr][a[i]] - s[sl - 1][a[i]]));
    for (int i = st(bl(r)); i <= r; ++i) smax(ans, (ll)b[a[i]] * (++cnt[a[i]] + s[sr][a[i]] - s[sl - 1][a[i]]));
    for (int i = l; i <= ed(bl(l)); ++i) cnt[a[i]] = 0;
    for (int i = st(bl(r)); i <= r; ++i) cnt[a[i]] = 0;
    return ans;
}

inline void lsh() {
    std::sort(b + 1, b + n + 1);
    dis = std::unique(b + 1, b + n + 1) - b - 1;
    for (int i = 1; i <= n; ++i) a[i] = std::lower_bound(b + 1, b + dis + 1, a[i]) - b;
}

inline void work() {
    lsh();
    ycl();
    while (Q--) {
        int l, r;
        read(l), read(r);
        printf("%lld\n", qry(l, r));
    }
}

inline void init() {
    read(n), read(Q);
    blo = sqrt(n);
    for (int i = 1; i <= n; ++i) read(a[i]), b[i] = a[i];
}

int main() {
#ifdef hzhkk
    freopen("hkk.in", "r", stdin);
#endif
    init();
    work();
    fclose(stdin), fclose(stdout);
    return 0;
}

原文地址:https://www.cnblogs.com/hankeke/p/bzoj4241.html