D. Powerful array

_Yxc發表於2024-05-15

https://codeforces.com/contest/86/problem/D

題意:n個數,m個查詢。每個查詢給出一個區間,查詢的結果是區間內每個數出現次數的平方*該數的總和。

思路:莫隊演算法。分塊,查詢排序,輸出。

總結:莫隊演算法適用於離線的情況,他的原理是將查詢按左端點分塊,塊的大小是數量的開平方。然後對查詢進行排序,塊不同,按塊id排序,在相同塊內,按右端點升序排序。這種排序規則可以讓右指標只往右邊走。這裡有一個非常重要的最佳化,就是在相鄰的塊內,右指標走到了最右邊,此時為了避免右指標需要回溯到最左邊,將下一個塊按右端點降序排序!這點非常重要。 在這種左右指標的移動規則下,直接暴力查詢即可!!太強了。

注意:塊所在id,一定是按左端點排序。 相鄰的塊一定要有個波浪線的右指標移動最佳化。

摘抄一段介紹過來:
原文連結:https://blog.csdn.net/ACpartner/article/details/75998670
莫隊演算法主要是利用了曼哈頓距離的長度關係,將每一個詢問的座標抽象為一個點,然後將詢問分塊,進行排序(分塊的原因是使得這些詢問座標的移動的曼哈頓距離達到 最小),排序之後再按照此時的順序進行左右區間的移動,而在內部的實現的過程就要看各個題目的要求了,所以在這裡主要是理解到,莫隊演算法的核心是:平面上移動曼哈頓距離最小 (利用分塊求得平面上曼哈頓距離的最小生成樹)+ 離線查詢(無法線上查詢),在解題的過程中也要想到使得每一次轉移計算答案的時間也要是O(1)的,到此,莫隊演算法也就完成。

int BLOCK_SIZE;
struct queryNode{
    int l, r, id, block_id;
    long long ans;

    queryNode(){}

    queryNode(int l_, int r_, int id_):
        l(l_),
        r(r_),
        id(id_),
        block_id(l / BLOCK_SIZE)
        {}

};

void solve(){
    int n, m;
    cin >> n >> m;

    vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i){
        cin >> a[i];
    }

    BLOCK_SIZE = sqrt(m);
    vector<queryNode> querys;
    for (int i = 1; i <= m; ++i){
        int l, r;
        cin >> l >> r;
        querys.emplace_back(l, r, i);
    }

    /*
      這個最佳化至關重要
    */
    sort(querys.begin(), querys.end(), [](const queryNode& a, const queryNode& b){
            return a.block_id != b.block_id ? a.block_id < b.block_id : a.block_id & 1 ? a.r < b.r : a.r > b.r;
         });
    int l = 1, r = 0;
    int s = *max_element(a.begin() + 1, a.end());
    vector<int> cnt(s + 1);
    long long ans = 0;
    auto cal = [&](int pos, int value){
        ans -= 1ll * cnt[pos] * cnt[pos] * pos;
        cnt[pos] += value;
        ans += 1ll * cnt[pos] * cnt[pos] * pos;
    };
    for (auto& q : querys){
        while (l < q.l) cal(a[l], -1), l ++;
        while (l > q.l) cal(a[l - 1],  1), l --;
        while (r > q.r) cal(a[r], -1), r --;
        while (r < q.r) cal(a[r + 1], 1), r++;
        q.ans = ans;
    }
    sort(querys.begin(), querys.end(), [](const queryNode& a, const queryNode& b){
            return a.id < b.id;
        });


    for (const auto& x : querys){
        cout << x.ans << '\n';
    }

}