1. 程式人生 > >[學習-思考-探究]莫隊算法 曼哈頓最小生成樹與分塊區間詢問算法-2

[學習-思考-探究]莫隊算法 曼哈頓最小生成樹與分塊區間詢問算法-2

iostream using space style 聯系 const ear math 模版

若要轉載,不需要聯系我,只需要在下面回復一下並註明原文。 在線區間詢問算法(增強算法) 考慮保存狀態 例題:小Z的襪子 如果對小Z的襪子應用基礎算法,會發生什麽? 小Z的襪子這道題目,僅僅知道區間[l, r]的答案是不能更新到[l, r+1]的。 為什麽?因為還要記錄每個區間的狀態,也就是每種顏色在區間內出現的次數。 顯然我們不能直接把狀態保存進每兩個端點構成的區間。 兩端點構成的區間有n個,一個狀態要n個變量,顯然空間是不能承受的。(實際上預處理時間也不能承受..) (當然你可以用之前所述的優化算法來實現..但是略慢一點,後面會一起講。) 註意到這個狀態滿足區間可減性。 例如1-10中有5個紅色,5個白色
1-5中有2個紅色,3個白色。 那麽6-10中有3個紅色,2個白色。 可以把狀態中每一個變量分別相減,也就是狀態滿足區間可減性。 同理狀態也滿足區間可加性。 那麽我們可以使用前綴和思想,維護序列某一個前綴的狀態。為了節省空間,我們只記錄了序列最左邊的點,到每一個塊端點的狀態。 具體計算過程,請看以下代碼
 for (int i = 0; i < n; i++) {
        insert(tans, tmaxn-2, col[i]);
        if ((i+1) % block_size == 0) {
            int b_in = (i+1
) / block_size; for (int j = 1; j <= n; j++) { pre[b_in].cnt[j] = st[tmaxn-2].cnt[j]; } } if (i % block_size == 0) { //這個循環的範圍僅針對小Z的襪子! int b_in = i / block_size; for (int j = 1; j <= n; j++) { update(tmaxn
-2, j); st[b_in].cnt[j] = st[tmaxn-2].cnt[j]; } clear(tmaxn-1); LL ans = 0; for (int j = i; j < n; j++) { insert(ans, tmaxn-1, col[j]); if (j % block_size == 0) { val[b_in][j/block_size] = ans; } } } }

pre就是這個前綴和。同時我們還把任意兩個塊端點之間的答案存入了val中。

考慮基礎算法中的做法,對於詢問,我們也從端點開始擴展,不過我們可以利用前綴和恢復狀態。顯然我們不能把n個變量一一恢復,會超時。

考慮懶惰思想,我們只記錄相減的兩個前綴的位置,當需要用到某一個變量的時候再更新。

該算法的復雜度是#O(nsqrt{n})#的。如果還沒有看懂,可以看以下代碼。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <vector>
#include <cstdio>

using namespace std;

//該模版需要狀態滿足區間可減性
//該模版寫的是 小Z的襪子 ...

typedef long long LL;

const int maxn = 55555;
const int tmaxn = 230;

const int stat_size = maxn;
int block_size;

struct stat {
    int cnt[stat_size];
    int ts_sub[stat_size];
    int tms_sub, suba, subb;
} st[tmaxn], pre[tmaxn];

inline void update(int x, int y) {
    if (st[x].ts_sub[y] < st[x].tms_sub) {
        st[x].ts_sub[y] = st[x].tms_sub;
        if (st[x].suba < 0) {
            st[x].cnt[y] = 0;
        } else st[x].cnt[y] = st[st[x].suba].cnt[y]-pre[st[x].subb].cnt[y];
    }
}

inline void sub(int x, int a, int b) {
    st[x].suba = a;
    st[x].subb = b;
    st[x].tms_sub ++;
}

inline void clear(int x) {
    sub(x, -1, -1);
}

inline void insert(LL &o, int x, int c) {
    update(x, c);
    //這裏是小z的襪子獨有寫法
    o -= (((LL)st[x].cnt[c])*(st[x].cnt[c]-1)) >> 1;
    st[x].cnt[c] ++;
    o += (((LL)st[x].cnt[c])*(st[x].cnt[c]-1)) >> 1;
}

LL val[tmaxn][tmaxn];

int col[maxn];

int n, m;

vector<int> ra, rb;

inline LL getAns(int l, int r) {
    int tmp_in = tmaxn-1;
    if (r-l+1 <= block_size) {
        clear(tmp_in);
        LL ret = 0;
        for (int i = l; i <= r; i++) {
            insert(ret, tmp_in, col[i]);
        }
        return ret;
    } else {
        int a = l, b = r;
        ra.clear(), rb.clear();
        while (a % block_size) {
            ra.push_back(a);
            a++;
        }
        while (b % block_size) {
            rb.push_back(b);
            b--;
        }
        int a_b = a / block_size;
        int b_b = b / block_size;
        LL ret = val[a_b][b_b];
        sub(tmp_in, b_b, a_b);
        for (int i = 0; i < ra.size(); i++) {
            insert(ret, tmp_in, col[ra[i]]);
        }
        for (int i = 0; i < rb.size(); i++) {
            insert(ret, tmp_in, col[rb[i]]);
        }
        return ret;
    }
}

LL gcd(LL a, LL b) {
    if (!b) return a;
    return gcd(b, a%b);
}

int main() {
    //freopen("sock.in", "r", stdin);
    //freopen("sock.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) {
        scanf("%d", &col[i]);
    }
    block_size = sqrt(n);
    LL tans = 0;
    for (int i = 0; i < n; i++) {
        insert(tans, tmaxn-2, col[i]);
        if ((i+1) % block_size == 0) {
            int b_in = (i+1) / block_size;
            for (int j = 1; j <= n; j++) {
                pre[b_in].cnt[j] = st[tmaxn-2].cnt[j];
            }
        }
        if (i % block_size == 0) {
            //這個循環的範圍僅針對小Z的襪子!
            int b_in = i / block_size;
            for (int j = 1; j <= n; j++) {
                update(tmaxn-2, j);
                st[b_in].cnt[j] = st[tmaxn-2].cnt[j];
            }
            clear(tmaxn-1);
            LL ans = 0;
            for (int j = i; j < n; j++) {
                insert(ans, tmaxn-1, col[j]);
                if (j % block_size == 0) {
                    val[b_in][j/block_size] = ans;
                }
            }
        }
    }
    for (int i = 1; i <= m; i++) {
        int l, r;
        scanf("%d%d", &l, &r);
        LL ans = getAns(l-1, r-1);
        LL len = r-l+1;
        if (ans == 0) {
            printf("0/1\n");
        } else {
            LL fm = len*(len-1)/2;
            LL g = gcd(ans, fm);
            printf("%lld/%lld\n", ans/g, fm/g);
        }
    }
    return 0;
}

[學習-思考-探究]莫隊算法 曼哈頓最小生成樹與分塊區間詢問算法-2