Bzoj P1176 [BOI2007]Mokia摩基亚___cdp分治套树状数组

15 篇文章 0 订阅
4 篇文章 0 订阅

题目大意:

维护一个W*W的矩阵,初始值均为S.
每次操作可以增加某格子的权值,或询问某子矩阵的总权值.
修改操作数M<=160000,询问数Q<=10000,W<=2000000.
保证答案不会超过int范围

分析:

给每个操作一个时间 t t t,第一个操作是1,第二个是2,以此类推
考虑对于一个询问 ( a x , a y , b x , b y , t ) (ax,ay,bx,by,t) (ax,ay,bx,by,t)
我们就是要找所有 ( x , y , z ) (x,y,z) (x,y,z)满足 a x &lt; = x &lt; = b x , a y &lt; = y &lt; = b y , z &lt; t ax&lt;=x&lt;=bx,ay&lt;=y&lt;=by,z&lt;t ax<=x<=bx,ay<=y<=by,z<t的增加操作的个数
坐标占两维,时间占一维
那么这个就是一个三维偏序问题,cdp分治套树状数组即可

代码:

#include <iostream>
#include <cmath>
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>

#define rep(i, st, ed) for (int i = st; i <= ed; i++)
#define rwp(i, ed, st) for (int i = ed; i >= st; i--)
#define mt(x) memset(x, 0, sizeof(x))

#define N 200005

using namespace std;

struct Node {
	int x, y, z, t, opt, mark;
}C[N*5], tmp[N*5];
int zyf[N], ans[N], m, cdp, tot, orz;

bool cmp(Node aa, Node bb) {
	if (aa.x == bb.x && aa.y == bb.y) return aa.t < bb.t;
	return (aa.x == bb.x) ? aa.y < bb.y : aa.x < bb.x;
}

void Ins(int x, int y) {
	for (; x <= orz; x += (x & (-x))) zyf[x] += y;
}

int Cal(int x) {
	int rp = 0;
	for (; x; x -= (x & (-x))) rp += zyf[x];
	return rp;
}

void cdq(int L, int R) {
	if (R - L <= 1) return;
	int mid = (L + R) >> 1;
	cdq(L, mid);
	cdq(mid, R);
	int p = L, q = mid, cnt = 0;	
	while (p < mid && q < R) {
		if (C[p].y <= C[q].y) { 
		   if (C[p].opt == 1) Ins(C[p].t + 1, C[p].z);
		   tmp[++cnt] = C[p++]; 
		} else {
		   	 if (C[q].opt == 2) ans[C[q].mark] -= Cal(C[q].t);
		   	    else if (C[q].opt == 3) ans[C[q].mark] += Cal(C[q].t);
		   	 tmp[++cnt] = C[q++];
		   } 
	}
	while (q < R) {
		if (C[q].opt == 2) ans[C[q].mark] -= Cal(C[q].t);
		   	else if (C[q].opt == 3) ans[C[q].mark] += Cal(C[q].t);
		tmp[++cnt] = C[q++];	
	}
	rep(i, L, p - 1) if (C[i].opt == 1) Ins(C[i].t + 1, -C[i].z);
	while (p < mid) tmp[++cnt] = C[p++];
	rep(i, 1, cnt) C[L + i - 1] = tmp[i];
}

int main() {
	scanf("%d %d", &m, &m); 
	int ax, ay, bx, by, opt;
	while (1) {
		scanf("%d", &opt);
		++orz;
		if (opt == 3) break;
		if (opt == 1) ++tot, scanf("%d %d %d", &C[tot].x, &C[tot].y, &C[tot].z), C[tot].opt = 1, C[tot].t = orz;
	    else {
	    	++cdp; 
		    scanf("%d %d %d %d", &ax, &ay, &bx, &by);
		    if (ax > bx || (ax == bx && ay > by)) ans[cdp] = 0; 
		       else { 
		    	if (ay > 1) C[++tot].x = bx, C[tot].y = ay - 1, C[tot].opt = 2, C[tot].mark = cdp, C[tot].t = orz;
				if (ax > 1) C[++tot].x = ax - 1, C[tot].y = by, C[tot].opt = 2, C[tot].mark = cdp, C[tot].t = orz; 
		        if (ax > 1 && ay > 1) C[++tot].x = ax - 1, C[tot].y = ay - 1, C[tot].opt = 3, C[tot].mark = cdp, C[tot].t = orz;
 	            C[++tot].x = bx, C[tot].y = by, C[tot].opt = 3, C[tot].mark = cdp, C[tot].t = orz;
			}
		}
	} 
	sort(C + 1, C + tot + 1, cmp);
	cdq(1, tot + 1);
	rep(i, 1, cdp) printf("%d\n", ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值