[bzoj1176][Balkan2007]Mokia cdq分治+树状数组

1176: [Balkan2007]Mokia

Time Limit: 30 Sec   Memory Limit: 162 MB
[ Submit][ Status][ Discuss]

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

0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3

Sample Output

3
5

HINT

保证答案不会超过int范围

Source


这道题的s是没有用的,不信看讨论
dalao说能用kd-tree搞,我不知道这kd-tree什么东西,但是为什么没有人写啊
对于cdq分治这个没有规范定义的东西也是不太懂
首先查询一个块可以四个块加加减减得到,应该都会吧...
那就先把一个查询分为四个部分
cdq是要离线操作的
按照x排序,以y为第二关键字
每次cdq分治时
按照时间二分区间
记得要清空树状数组
#include<algorithm>
#include<cstdio>
const int N = 2000000 + 5;
const int M = 10005;
int s,w,n=0,ans[M],c[N];
struct data{
	int x,y,type,pos,v,id;
}a[N/2],b[N/2];
bool cmp( data a, data b ){
	if( a.x == b.x && a.y == b.y ) return a.type < b.type;
	if( a.x == b.x ) return a.y < b.y;
	return a.x < b.x;
}
void modify( int k, int v ){
	for( int i = k; i <= w; i += i&-i ) c[i] += v;
}
int query( int k ){
	int res = 0;
	for( int i = k; i; i -= i&-i ) res += c[i];
	return res;
}
void cdq( int l, int r ){
	if( l == r ) return ;
	int mid = (l+r)>>1, t1 = l-1, t2 = mid;
	for( int i = l; i <= r; i++ ){
		if( a[i].id <= mid && !a[i].type ) modify(a[i].y,a[i].v);
		if( a[i].id >  mid &&  a[i].type ) ans[a[i].pos] += a[i].v*query(a[i].y);
	}
	for( int i = l; i <= r; i++ ) if( a[i].id <= mid && !a[i].type ) modify(a[i].y,-a[i].v);
	for( int i = l; i <= r; i++ ) if( a[i].id <= mid ) b[++t1] = a[i]; else b[++t2] = a[i];
	for( int i = l; i <= r; i++ ) a[i] = b[i];
	cdq(l,mid); cdq(mid+1,r);
}
int main(){
	scanf("%d%d", &s, &w);
	while(1){
		int opt,x1,y1,x2,y2;
		scanf("%d", &opt);
		if( opt == 1 ){
			n++; scanf("%d%d%d", &a[n].x, &a[n].y, &a[n].v); a[n].id = n;
		}
		if( opt == 2 ){
			scanf("%d%d%d%d", &x1, &y1, &x2, &y2); ans[0]++;
			n++; a[n].x = x2;   a[n].y = y2;   a[n].v = 1;  a[n].type = 1; a[n].id = n; a[n].pos = ans[0];
			n++; a[n].x = x1-1; a[n].y = y1-1; a[n].v = 1;  a[n].type = 1; a[n].id = n; a[n].pos = ans[0];
			n++; a[n].x = x2;   a[n].y = y1-1; a[n].v = -1; a[n].type = 1; a[n].id = n; a[n].pos = ans[0];
			n++; a[n].x = x1-1; a[n].y = y2;   a[n].v = -1; a[n].type = 1; a[n].id = n; a[n].pos = ans[0];
		}
		if( opt == 3 ) break;
	}
	std::sort(a+1,a+n+1,cmp); cdq(1,n);
	for( int i = 1; i <= ans[0]; i++ ) printf("%d\n", ans[i]);
	return 0;
}
总结
以后写一道题要写总结了
分治时把'1'和'l'分别开,什么时候是1,什么时候是l
对于有些相似操作(比如modify和change),拷贝过来时要检查变量有没有改完
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值