BZOJ4241 历史研究

 

[传送门]

感觉这种信息不符合区间可加性,或者用来区间相加的时间复杂度太高的话就直接分块。
res[i][j] 表示第 $i$ 块到第 $j$ 块之间的答案,查询就先查询整块,再对两端暴力。
注意不要用memset清空cnt数组,每次使用了再循环一遍撤销操作就行了。

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

namespace IO {
    const int MAXSIZE = 1 << 20;
    char buf[MAXSIZE], *p1, *p2;
    #define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin), p1 == p2) ? EOF : *p1++)
    template<typename T>
    inline void read(T &x) {
        x = 0; T f = 1; char ch = gc();
        while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = gc(); }
        while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = gc(); }
        x *= f;    
    }
} using namespace IO;

const int N = 1e5 + 7;
const int BLOCK = sqrt(N) + 7;

int n, q, a[N], v[N], tol;
int l[BLOCK], r[BLOCK], block, num, belong[N];
ll res[BLOCK][BLOCK], cnt[N], sum[BLOCK][N];

template<class T>
inline void checkmax(T &a, T b) {
    if (a < b) a = b;
}

void build() {
    block = sqrt(n);
    num = n / block;
    if (n % block) num++;
    for (int i = 1; i <= num; i++)
        l[i] = (i - 1) * block + 1, r[i] = i * block;
    r[num] = n;
    for (int i = 1; i <= num; i++)
        for (int j = l[i]; j <= r[i]; j++)
            belong[j] = i;
    for (int i = 1; i <= num; i++) {
        for (int j = 1; j <= tol; j++)
            cnt[j] = 0, sum[i][j] = sum[i - 1][j];
        ll mx = 0;
        for (int j = l[i]; j <= n; j++) {
            cnt[a[j]] += v[a[j]];
            checkmax(mx, cnt[a[j]]);
            res[i][belong[j]] = mx;
            if (j <= r[i]) sum[i][a[j]] += v[a[j]];
        }
    }
    for (int i = 1; i <= tol; i++)
        cnt[i] = 0;
}

bool vis[N];

ll query(int x, int y) {
    int p = belong[x], q = belong[y];
    if (p == q) {
        ll ans = 0;
        for (int i = x; i <= y; i++)
            cnt[a[i]] += v[a[i]], checkmax(ans, cnt[a[i]]);
        for (int i = x; i <= y; i++)
            cnt[a[i]] -= v[a[i]];
        return ans;
    }
    ll ans = res[p + 1][q - 1];
    if (p + 1 <= q - 1) {
        for (int i = x; i <= r[p]; i++)
            if (!vis[a[i]]) 
                cnt[a[i]] += sum[q - 1][a[i]] - sum[p][a[i]], vis[a[i]] = 1;
        for (int i = l[q]; i <= y; i++)
            if (!vis[a[i]])
                cnt[a[i]] += sum[q - 1][a[i]] - sum[p][a[i]], vis[a[i]] = 1;    
    }
    for (int i = x; i <= r[p]; i++) {
        cnt[a[i]] += v[a[i]];
        checkmax(ans, cnt[a[i]]);
    }
    for (int i = l[q]; i <= y; i++) {
        cnt[a[i]] += v[a[i]];
        checkmax(ans, cnt[a[i]]);
    }
    for (int i = x; i <= r[p]; i++) {
        cnt[a[i]] = 0;
        vis[a[i]] = 0;
    }
    for (int i = l[q]; i <= y; i++) {
        cnt[a[i]] = 0;
        vis[a[i]] = 0;
    }
    return ans;
}

int main() {
    read(n), read(q);
    for (int i = 1; i <= n; i++)
        read(a[i]), v[i] = a[i];
    sort(v + 1, v + 1 + n);
    tol = unique(v + 1, v + 1 + n) - v - 1;
    for (int i = 1; i <= n; i++)
        a[i] = lower_bound(v + 1, v + 1 + tol, a[i]) - v;
    build();
    for (int l, r; q--; ) {
        read(l), read(r);
        printf("%lld\n", query(l, r));
    }
    return 0;
}
View Code

 

posted @ 2019-10-14 21:34  Mrzdtz220  阅读(103)  评论(0编辑  收藏  举报