bzoj 2038 [2009国家集训队]小Z的袜子(hose) 莫队

题面

题目传送门

解法

可以说是莫队裸题了吧

莫队算法的本质是,将所有询问的区间按照一定顺序排序,然后不断通过已知区间更新后面的区间

假设现在已经做到区间\([l_1,r_1]\),下一个区间是\([l_2,r_2]\)

那么就暴力将区间从\([l_1,r_1]\)扩展至\([l_2,r_2]\),并在扩展的时候更新答案

更新答案对于此题比较简单,直接\(O(1)\)计算即可

每一次更新的复杂度为\(O(|l_1-l_2|+|r_1-r_2|)\)

所以我们要以一个比较适当的顺序,使得这个加起来的总和尽量小

我排序的方式比较鬼畜,是看某位卡常dalao写的

时间复杂度:\(O(n\sqrt m)\)

代码

#include <bits/stdc++.h>
#define int long long
#define N 50010
using namespace std;
template <typename node> void read(node &x) {
    x = 0; int f = 1; char c = getchar();
    while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
    while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
    int l, r, id;
} b[N];
struct Ans {
    int x, y;
} ans[N];
int sum, a[N], s[N], col[N];
bool cmp(Node x, Node y) {
    return col[x.l] ^ col[y.l] ? col[x.l] < col[y.l] : col[x.l] & 1 ? x.r < y.r : x.r > y.r;
}
void update(int x, int v) {
    sum += 2 * s[a[x]] * v - v + v * v;
    s[a[x]] += v;
}
main() {
    int n, q; read(n), read(q);
    for (int i = 1; i <= n; i++) read(a[i]);
    for (int i = 1; i <= q; i++)
        read(b[i].l), read(b[i].r), b[i].id = i;
    int siz = n / sqrt(q * 2 / 3);
    for (int i = 1; i <= q; i++) col[i] = (i + siz - 1) / siz;
    sort(b + 1, b + q + 1, cmp);
    for (int i = 1, l = 1, r = 0; i <= q; i++) {
        while (r < b[i].r) update(++r, 1);
        while (r > b[i].r) update(r--, -1);
        while (l < b[i].l) update(l++, -1);
        while (l > b[i].l) update(--l, 1);
        if (!sum) {ans[b[i].id] = (Ans) {0, 1}; continue;}
        int x = sum, y = (b[i].r - b[i].l + 1) * (b[i].r - b[i].l);
        int t = __gcd(x, y); ans[b[i].id] = (Ans) {x / t, y / t};
    }
    for (int i = 1; i <= q; i++) cout << ans[i].x << '/' << ans[i].y << "\n";
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/copperoxide/p/9478677.html