zoukankan      html  css  js  c++  java
  • bzoj 1176 cdq分治套树状数组

    题面:

    维护一个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的答案

    思路:正常思路用树状数组套树状数组,但是w范围过大,开不下。考虑用cdq分治套树状数组。我们用cdq分治对x坐标排序,用树状数组维护y坐标,把询问用差分思想拆分成四个不同的询问即可。

    代码:

    #include <bits/stdc++.h>
    #define LL long long
    #define lowbit (x & (-x))
    using namespace std;
    const int maxn = 200010;
    int tot, tot_ans, n;
    struct query{
    	int type, x, y, pos, flag;
    };
    query q[maxn], tmp[maxn];
    int c[maxn * 10], ans[maxn];
    void add(int x, int y) {
    	for (; x <= n; x += lowbit)
    		c[x] += y;
    }
    int ask(int x) {
    	int ans = 0;
    	for (; x; x-= lowbit) {
    		ans += c[x];
    	}
    	return ans;
    }
    void clear(int x) {
    	for (; x <= n; x += lowbit) {
    		c[x] = 0;
    	}
    }
    void cdq(int l, int r) {
    	if(l == r) return;
    	int mid = (l + r) >> 1;
    	cdq(l, mid);
    	cdq(mid + 1, r);
    	int l1 = l, l2 = mid + 1, pos = l;
    	while(l1 <= mid && l2 <= r) {
    		if(q[l1].x <= q[l2].x) {
    			if(q[l1].type == 1) {
    				add(q[l1].y, q[l1].pos);
    			}
    			tmp[pos++] = q[l1++];
    		} else {
    			if(q[l2].type == 2) {
    				ans[q[l2].pos] += q[l2].flag * ask(q[l2].y);
    			}
    			tmp[pos++] = q[l2++];
    		}
    	}
    	while(l1 <= mid) tmp[pos++] = q[l1++];
    	while(l2 <= r) {
    		if(q[l2].type == 2) {
    			ans[q[l2].pos] += q[l2].flag * ask(q[l2].y);
    		}
    		tmp[pos++] = q[l2++];
    	}
    	for (int i = l; i <= mid; i++) {
    		if(q[i].type == 1) {
    			clear(q[i].y);
    		}
    	}
    	for (int i = l; i <= r; i++)
    		q[i] = tmp[i];
    }
    int main() {
    	int l1, r1, l2, r2, x, op, val;
    	scanf("%d%d", &x, &n);
    	while(~scanf("%d", &op) && op != 3) {
    		if(op == 1) {
    			scanf("%d%d%d", &l1, &r1, &val);
    			q[++tot] = (query){1, l1, r1, val, 0};
    		} else {
    			scanf("%d%d%d%d", &l1, &r1, &l2, &r2);
    			tot_ans++;
    			ans[tot_ans] = x * (l2 - l1 + 1) * (r2 - r1 + 1);
    			q[++tot] = (query){2, l1 - 1, r1 - 1, tot_ans, 1};
    			q[++tot] = (query){2, l1 - 1, r2, tot_ans, -1};
    			q[++tot] = (query){2, l2, r1 - 1, tot_ans, -1};
    			q[++tot] = (query){2, l2, r2, tot_ans, 1};
    		}
    	}
    	cdq(1, tot);
    	for (int i = 1; i <= tot_ans; i++) {
    		printf("%d
    ", ans[i]);
    	}
    } 
    

      

  • 相关阅读:
    【vim使用】光标的 段落切换、括号切换
    【vim使用】行数跳转 和 屏幕移动
    【vim使用】vim的三种工作模式
    【vim使用】vim的异常处理,删除交换文件
    【vim使用】打开文件,并且定位行
    【vim使用】vim新建文件、打开文件
    【vim使用】vim简介
    【终端使用】SSH免密码登录——02
    【终端使用】SSH免密码登录——01
    【终端使用】"apt"命令 安装/卸载软件
  • 原文地址:https://www.cnblogs.com/pkgunboat/p/11161134.html
Copyright © 2011-2022 走看看