Codeforces 86D 题解(莫队)

Description

传送门

给出一个长度为\(n\)数列\(a\)\(q\)次询问,每次询问为一个\([l,r]\)的区间,求区间内 每种数字出现次数的平方×数字的值 的和。

\(n,q \leq 2 \times 10^5, a_i \leq 10^6\)

Solution

看到只有询问没有修改,而且有\(a_i \leq 10^6\),那么我们可以使用莫队算法。

记一个now表示当前区间的答案,在区间伸长的时候加一下伸长的位置的答案,缩短的时候减一下缩短的位置的答案就好了。

Code

AC Submission

#include <algorithm>
#include <cstdio>
#include <cmath>
#define rep(a,b,c) for(int a=b;a<=c;a++)
#define int long long
const int MAXN = 1e6 + 5;
int n, m, a[MAXN], cnt[MAXN], now, block, ans[MAXN], ql = 1, qr;
struct Query {int l, r, ser;} q[MAXN];
inline bool cmp(Query a, Query b) {
    return (a.l / block == b.l / block) ? a.r < b.r : a.l < b.l;
}
inline void ADD(int pos) {
    cnt[a[pos]]++; now += a[pos] * (2 * cnt[a[pos]] - 1);
}
inline void DEL(int pos) {
    cnt[a[pos]]--; now -= a[pos] * (2 * cnt[a[pos]] + 1);
}
signed main() {
    scanf("%lld %lld", &n, &m); block = std::sqrt(n);
    rep(i, 1, n) scanf("%lld", &a[i]);
    rep(i, 1, m) {
        int l, r; scanf("%lld %lld", &l, &r);
        q[i] = (Query) {l, r, i};
    } std::sort(q + 1, q + m + 1, cmp);
    rep(i, 1, m) {
        int L = q[i].l, R = q[i].r;
        while(ql < L) DEL(ql++); while(ql > L) ADD(--ql);
        while(qr < R) ADD(++qr); while(qr > R) DEL(qr--);
        ans[q[i].ser] = now;
    } rep(i, 1, m) printf("%lld\n", ans[i]);
    return 0;
}
posted @ 2020-04-13 15:27  hqsan  阅读(150)  评论(0)    收藏  举报