【算法】分塊——教主的魔法&不勤勞的圖書管理員
阿新 • • 發佈:2018-03-01
clu clas pos getc 修改 我們 就是 暴力枚舉 c++
由不勤勞的圖書管理員帶入了分塊的坑,深深地被其暴力與優雅所征服。分塊的實質就是將暴力塊狀封裝起來,一整塊的部分就一整塊處理,零碎的部分就怎麽暴力怎麽來。因為分塊大小的原因,限制了零碎部分數據的數量級,所以復雜度得以保證。
1.教主的魔法:可以算得上是一個分塊的板子題。對於每一個塊內sort排序,保存id值。對於修改,塊內的找到點暴力修改之後重新排序,一整個塊的不改變相對大小關系,所以直接外部記錄累加的值。查詢也一樣,塊外暴力,塊內二分查找。
#include <bits/stdc++.h> using namespace std; #define int long long #definemaxn 2000000 #define maxb 1050 int n, q, B, sum[maxb], a[maxn], cnt[maxb]; struct node { int v, id; }c[maxb][maxb]; int read() { int x = 0; char c; c = getchar(); while(c < ‘0‘ || c > ‘9‘) c = getchar(); while(c >= ‘0‘ && c <= ‘9‘) x = x * 10 + c - ‘0‘, c = getchar();return x; } bool cmp(node a, node b) { return a. v < b.v; } void Vio_C(int b, int x, int w) { c[b][x].v += w; } void Change(int L, int R, int W) { int a = L / B, b = R / B; if(a == b) { for(int i = 1; i <= cnt[a]; i ++) if(c[a][i].id >= L && c[a][i].id <= R) Vio_C(a, i, W); sort(c[a]+ 1, c[a] + cnt[a] + 1, cmp); } for(int i = 1; i <= cnt[a]; i ++) if(c[a][i].id >= L) Vio_C(a, i, W); sort(c[a] + 1, c[a] + cnt[a] + 1, cmp); for(int i = 1; i <= cnt[b]; i ++) if(c[b][i].id <= R) Vio_C(b, i, W); sort(c[b] + 1, c[b] + cnt[b] + 1, cmp); for(int i = a + 1; i < b; i ++) sum[i] += W; } int check(int b, int C) { C -= sum[b]; int ans = cnt[b] + 1, l = 1, r = cnt[b]; while(l <= r) { int mid = (l + r) >> 1; if(c[b][mid].v >= C) ans = mid, r = mid - 1; else l = mid + 1; } return cnt[b] - ans + 1; } void Query(int L, int R, int C) { int a = L / B, b = R / B; int ans = 0; if(a == b) { C -= sum[a]; for(int i = 1; i <= cnt[a]; i ++) if(c[a][i].id >= L && c[a][i].id <= R && c[a][i].v >= C) ans ++; printf("%lld\n", ans); return; } for(int i = 1; i <= cnt[a]; i ++) if(c[a][i].id >= L && c[a][i].v + sum[a] >= C) ans ++; for(int i = 1; i <= cnt[b]; i ++) if(c[b][i].id <= R && c[b][i].v + sum[b] >= C) ans ++; for(int i = a + 1; i < b; i ++) ans += check(i, C); printf("%lld\n", ans); return; } signed main() { n = read(), q = read(); B = sqrt(n); for(int i = 1; i <= n; i ++) { a[i] = read(); int block = i / B; c[block][++ cnt[block]].v = a[i]; c[block][cnt[block]].id = i; if(((i / B) != (i + 1) / B) || i == n) sort(c[block] + 1, c[block] + cnt[block] + 1, cmp); } for(int i = 1; i <= q; i ++) { char c; cin >> c; int L = read(), R = read(), W = read(); if(c == ‘M‘) Change(L, R, W); else Query(L, R, W); } return 0; }
2.不勤勞的圖書管理員
這題首先註意到交換a,b的位置,只會影響到a,b之間的數。在一段區間裏,a對雜亂值的貢獻是多少?不難發現=在a之前且應當在a之後的書本雜亂值之和+v[a]*前面書本的個數。所以我們對每一個塊使用兩個樹狀數組,一個紀錄個數,一個記錄頁數的前綴和,塊內的利用樹狀數組快速查詢,塊外的暴力枚舉計算即可。
因為此題代碼是Kuai的(那個時候還不會寫分塊),所以就不貼代碼啦。
【算法】分塊——教主的魔法&不勤勞的圖書管理員