首页 > 代码库 > bzoj 1176 [Balkan2007]Mokia - CDQ分治 - 树状数组
bzoj 1176 [Balkan2007]Mokia - CDQ分治 - 树状数组
Description
维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.
Input
第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小
接下来每行为一下三种输入之一(不包含引号):
"1 x y a"
"2 x1 y1 x2 y2"
"3"
输入1:你需要把(x,y)(第x行第y列)的格子权值增加a
输入2:你需要求出以左下角为(x1,y1),右上角为(x2,y2)的矩阵内所有格子的权值和,并输出
输入3:表示输入结束
Output
对于每个输入2,输出一行,即输入2的答案
Sample Input
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
Sample Output
5
HINT
保证答案不会超过int范围
题目大意 (数据结构题题面太简洁不需要大意)
显然树套树加动态开点可以过掉这道题,然而表示并不想写。
由于支持离线可以考虑CDQ分治。
首先对于一个左上角在(1, 1),右下角在(x, y)的矩阵的权值和,是可以求出来的。首先以x为第一关键字,y为第二关键字,opt为第三关键字排序,然后用树状数组维护y = i上的和。
所以我们将一个询问拆成4个询问,再根据简单容斥计算出。
Code
1 /** 2 * bzoj 3 * Problem#1176 4 * Accepted 5 * Time:6964ms 6 * Memory:33940k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 #ifndef WIN32 12 #define Auto "%lld" 13 #else 14 #define Auto "%I64d" 15 #endif 16 17 #define lowbit(x) (x) & (-x) 18 #define LL long long 19 20 typedef class IndexedTree { 21 public: 22 int s; 23 LL *lis; 24 25 IndexedTree() { } 26 IndexedTree(int s):s(s) { 27 lis = new LL[(s + 1)]; 28 memset(lis, 0, sizeof(LL) * (s + 1)); 29 } 30 31 inline void add(int idx, int x) { 32 for(; idx <= s; idx += lowbit(idx)) 33 lis[idx] += x; 34 } 35 36 inline LL getSum(int idx) { 37 LL ret = 0; 38 for(; idx; idx -= lowbit(idx)) 39 ret += lis[idx]; 40 return ret; 41 } 42 }IndexedTree; 43 44 typedef class Query { 45 public: 46 int x; 47 int y; 48 int opt; 49 int sign; 50 int id; 51 52 Query(int x = 0, int y = 0, int opt = 0, int sign = 0, int id = 0):x(x), y(y), opt(opt), sign(sign), id(id) { } 53 54 boolean operator < (Query b) const { 55 if(x != b.x) return x < b.x; 56 if(y != b.y) return y < b.y; 57 return opt < b.opt; 58 } 59 }Query; 60 61 int s, w; 62 int cnt = 0; 63 IndexedTree it; 64 vector<Query> qs; 65 vector<Query> bq; 66 LL *res; 67 68 inline void init() { 69 int opt, x0, y0, x1, y1, c; 70 scanf("%d%d", &s, &w); 71 while(~scanf("%d", &opt) && opt != 3) { 72 scanf("%d%d", &x0, &y0); 73 if(opt == 1) { 74 scanf("%d", &c); 75 qs.push_back(Query(x0, y0, opt, c, cnt)); 76 } else { 77 scanf("%d%d", &x1, &y1); 78 qs.push_back(Query(x1, y1, opt, 1, cnt)); 79 qs.push_back(Query(x0 - 1, y1, opt, -1, cnt)); 80 qs.push_back(Query(x1, y0 - 1, opt, -1, cnt)); 81 qs.push_back(Query(x0 - 1, y0 - 1, opt, 1, cnt)); 82 bq.push_back(Query(x0, y0, x1, y1, cnt)); 83 } 84 cnt++; 85 } 86 } 87 88 void CDQDividing(int l, int r, vector<Query> &que) { 89 if(que.empty()) return; 90 if(l == r) return; 91 92 int mid = (l + r) >> 1; 93 for(int i = 0; i < (signed)que.size(); i++) { 94 if(que[i].opt == 1 && que[i].id <= mid) 95 it.add(que[i].y, que[i].sign); 96 if(que[i].opt == 2 && que[i].id > mid) 97 res[que[i].id] += it.getSum(que[i].y) * que[i].sign; 98 } 99 100 for(int i = 0; i < (signed)que.size(); i++)101 if(que[i].opt == 1 && que[i].id <= mid)102 it.add(que[i].y, -que[i].sign);103 104 vector<Query> ql, qr;105 for(int i = 0; i < (signed)que.size(); i++)106 if(que[i].id <= mid)107 ql.push_back(que[i]);108 else109 qr.push_back(que[i]);110 111 que.clear();112 CDQDividing(l, mid, ql);113 CDQDividing(mid + 1, r, qr);114 }115 116 inline void solve() {117 res = new LL[(cnt + 1)];118 memset(res, 0, sizeof(LL) * (cnt + 1));119 sort(qs.begin(), qs.end());120 it = IndexedTree(w);121 CDQDividing(0, cnt - 1, qs);122 123 for(int i = 0; i < (signed)bq.size(); i++)124 printf(Auto"\n", res[bq[i].id] + (bq[i].opt - bq[i].x + 1) * (bq[i].sign - bq[i].y + 1) * s);125 }126 127 int main() {128 init();129 solve();130 return 0;131 }
Description
维护一个W*W的矩阵,初始值均为S.每次操作可以增加某格子的权值,或询问某子矩阵的总权值.修改操作数M<=160000,询问数Q<=10000,W<=2000000.
Input
第一行两个整数,S,W;其中S为矩阵初始值;W为矩阵大小
接下来每行为一下三种输入之一(不包含引号):
"1 x y a"
"2 x1 y1 x2 y2"
"3"
输入1:你需要把(x,y)(第x行第y列)的格子权值增加a
输入2:你需要求出以左下角为(x1,y1),右上角为(x2,y2)的矩阵内所有格子的权值和,并输出
输入3:表示输入结束
Output
对于每个输入2,输出一行,即输入2的答案
Sample Input
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3
Sample Output
5
HINT
保证答案不会超过int范围
bzoj 1176 [Balkan2007]Mokia - CDQ分治 - 树状数组