[學習-思考-探究]莫隊算法 曼哈頓最小生成樹與分塊區間詢問算法-3
阿新 • • 發佈:2017-08-13
tdi push_back col none ast 查找 循環 pac 生成
若要轉載,不需要聯系我,只需要在下面回復一下並註明原文。
在線區間詢問算法(增強算法)2
#include <iostream> #include <algorithm> #include <cmath> #include <vector> #include <cstdio> using namespace std; //該模版需要狀態滿足區間可減性 //該模版寫的是 小Z的襪子 ... typedef long long LL; const int maxn = 50005; const int tmaxn = 888; intblock_size; struct Val { int t, v; Val(int t_, int v_) : t(t_), v(v_) {} bool operator<(Val b) const { return t < b.t; } }; vector<Val> stat[tmaxn][maxn]; int GetInt() { int x = 0, F = 1; char C = getchar(); while (C < ‘0‘ || C > ‘9‘) { if (C == ‘-‘) F = -F; C = getchar(); } while (C >= ‘0‘ && C <= ‘9‘) { x = x * 10 - ‘0‘ + C, C = getchar(); } return x * F; } inline int getLast(int b, int in) { if (stat[b][in].empty()) return 0; vector<Val>::iterator iter = stat[b][in].end(); return (--iter)->v; } inlinevoid insert(LL &o, int b, int in, int t) { //這裏是小z的襪子獨有寫法 o -= (getLast(b, in)*(getLast(b, in)-1)) >> 1; stat[b][in].push_back(Val(t, getLast(b, in)+1)); o += (getLast(b, in)*(getLast(b, in)-1)) >> 1; } LL val[tmaxn][tmaxn]; int col[maxn]; int n, m; vector<int> ra, rb; int cnt[maxn]; int ts[maxn]; int tms = 0; inline void update_clear(int in) { if (ts[in] < tms) { ts[in] = tms; cnt[in] = 0; } } int lb = 0, ri = 0; inline void update_restore(int in) { if (ts[in] < tms) { ts[in] = tms; if (stat[lb][in].empty()) cnt[in] = 0; else { int t = int(lower_bound(stat[lb][in].begin(), stat[lb][in].end(), Val(ri, 0))-stat[lb][in].begin()); if (stat[lb][in][t].t > ri) t--; if (t < 0) cnt[in] = 0; else cnt[in] = stat[lb][in][t].v; } } } inline LL getAns(int l, int r) { if (r-l+1 <= block_size) { ++ tms; LL ret = 0; for (int i = l; i <= r; i++) { update_clear(col[i]); ret -= (cnt[col[i]]*(cnt[col[i]]-1)) >> 1; cnt[col[i]] ++; ret += (cnt[col[i]]*(cnt[col[i]]-1)) >> 1; } 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]; lb = a_b; ri = b; ++ tms; for (int i = 0; i < ra.size(); i++) { update_restore(col[ra[i]]); ret -= (cnt[col[ra[i]]]*(cnt[col[ra[i]]]-1)) >> 1; cnt[col[ra[i]]] ++; ret += (cnt[col[ra[i]]]*(cnt[col[ra[i]]]-1)) >> 1; } for (int i = 0; i < rb.size(); i++) { update_restore(col[rb[i]]); ret -= (cnt[col[rb[i]]]*(cnt[col[rb[i]]]-1)) >> 1; cnt[col[rb[i]]] ++; ret += (cnt[col[rb[i]]]*(cnt[col[rb[i]]]-1)) >> 1; } 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); n = GetInt(), m = GetInt(); for (int i = 0; i < n; i++) { col[i] = GetInt(); } block_size = sqrt(n/(log(n)/log(2)+1)); for (int i = 0; i < n; i++) { if (i % block_size == 0) { //這個循環的範圍僅針對小Z的襪子! int b_in = i / block_size; LL ans = 0; for (int j = i; j < n; j++) { insert(ans, b_in, col[j], j); if (j % block_size == 0) { val[b_in][j/block_size] = ans; } } } } for (int i = 1; i <= m; i++) { int l = GetInt(), r = GetInt(); 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; }
這一段代碼空間太大不能AC,但是非常有用,具有普遍性。如果不想看可以直接看下一篇,比較重要和有效。
這道題不是已經用在線算法A過了,怎麽又來一遍?
別忘了!之前用到了區間可加性!
這次的算法既不用區間可加性,也不用區間可減性,是怎麽做到的呢?
從一個端點出發,到每一個點的區間,可以依次向又更新,每次最多更新一個點。
因此我們可以使用可持久化思想。
這裏是用的是一種亂想出來的“可持久化”數組。通過vector記錄每一項的修改,然後通過二分查找的方式,獲得某一時間的狀態。
這樣查詢復雜度多了一個log。
引用以前寫的說明:
嗯,於是我們就成功把log放進了根號了。
如果你還沒有看懂,或者有興趣與我討論,可以加QQ2502669375 下一篇鏈接:[學習-思考-探究]莫隊算法 曼哈頓最小生成樹與分塊區間詢問算法-4
[學習-思考-探究]莫隊算法 曼哈頓最小生成樹與分塊區間詢問算法-3