BZOJ 2683: 简单题(CDQ分治)

本文详细介绍了一种解决二维更新查询问题的有效算法——CDQ分治,并提供了完整的代码实现。通过对CDQ分治原理的剖析,文章解释了如何利用该算法处理大规模数据集上的复杂查询与更新操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2683: 简单题

Time Limit: 50 Sec  Memory Limit: 128 MB
Submit: 1613  Solved: 651
[ Submit][ Status][ Discuss]

Description

你有一个N*N的棋盘,每个格子内有一个整数,初始时的时候全部为0,现在需要维护两种操作:

 

命令

参数限制

内容

1 x y A

1<=x,y<=N,A是正整数

将格子x,y里的数字加上A

2 x1 y1 x2 y2

1<=x1<= x2<=N

1<=y1<= y2<=N

输出x1 y1 x2 y2这个矩形内的数字和

3

终止程序

Input

输入文件第一行一个正整数N。
接下来每行一个操作。
 

Output

对于每个2操作,输出一个对应的答案。
 

Sample Input

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

1<=N<=500000,操作数不超过200000个,内存限制20M。

对于100%的数据,操作1中的A不超过2000。

Source


题解:像这种题第一感觉是二维树状数组,然而n有100000,开不了这么大的数组。
当然,kd树也是可以的,然而我并不会,这里采用一种新姿势:CDQ分治。
看了许多博客和PPT,算是懂的差不多了,看完各种大佬们的讲解,发现cdq分治怎么
和归并排序这么像? 以为是自己yy,其实就是这样。归并排序就是一种简单的cdq分治。
呢就讲一下cdq分治喽。
一篇关于cdq分治的很好的讲解博客:http://www.cnblogs.com/mlystdcall/p/6219421.html

CDQ分治:
优点:可以顶替复杂的高级数据结构,而且常数比较小
缺点:必须离线操作
基本思想:将查询和修改看做整体,然后对其排序,设这个整体的区间是[l,r],然后我们
可以将问题进行分治,比如分成区间[l,m]和[m+1,r]。这样一个主问题就分解成两个子问题了,
但是你会有疑问了,呢前半部分区间对后半部分区间产生影响怎么办? 没错,这就是cdq分治和
普通分治最根本的区别所在。分治的区间是会相互影响的(当然后边不会影响前边的)。
说白了cdq分治就是将一个问题分解成两个子问题,然后枚举前半区间对后半区间的影响,之后
通过递归在分别仿照之前求解若干子区间的过程(当然你可以从小到大递归)。
(可能说的不是很对,我是这样理解的)
#include<set>  
#include<map>     
#include<stack>            
#include<queue>            
#include<vector>    
#include<string> 
#include<time.h>
#include<math.h>            
#include<stdio.h>            
#include<iostream>            
#include<string.h>            
#include<stdlib.h>    
#include<algorithm>   
#include<functional>    
using namespace std;            
#define ll long long        
#define inf  1000000000       
#define mod 1000000007             
#define maxn  100010
#define lowbit(x) (x&-x)            
#define eps 1e-9 
struct node
{
	int x,y,id,xd,siz,val;
	bool operator <(const node &b) const
	{
		if(x<b.x || x==b.x && y<b.y || x==b.x && y==b.y && siz<b.siz)
			return 1;
		return 0;
	}
}q[maxn*8],a[maxn*8];
int n,ans[maxn*8],num,sum[maxn*8];
void update(int x,int val)
{
	while(x<=n)
	{
		sum[x]+=val;
		x+=lowbit(x);
	}
}
int query(int x)
{
	int res=0;
	while(x)
	{
		res+=sum[x];
		x-=lowbit(x);
	}
	return res;
}
void cdq(int l,int r)
{
	if(l==r)
		return;
	int i,m=(l+r)/2;
	for(i=l;i<=r;i++)
	{
		if(a[i].siz==1 && a[i].id<=m)
			update(a[i].y,a[i].val);
		if(a[i].siz==2 && a[i].id>m)
			ans[a[i].xd]+=query(a[i].y)*a[i].val;
	}
	for(i=l;i<=r;i++)
		if(a[i].siz==1 && a[i].id<=m)
			update(a[i].y,-a[i].val);
	int lq=l,rq=m+1;
	for(i=l;i<=r;i++)
	{
		if(a[i].id<=m)
			q[lq++]=a[i];
		else
			q[rq++]=a[i];
	}
	for(i=l;i<=r;i++)
		a[i]=q[i];
	cdq(l,m);cdq(m+1,r);
}
int main(void)
{
	int i,j,cnt=0,t,x,y,xx,yy,z;
	scanf("%d",&n);
	while(1)
	{
		scanf("%d",&t);
		if(t==1)
		{
			scanf("%d%d%d",&x,&y,&z);
			a[++num].x=x;a[num].y=y;a[num].val=z;a[num].id=num;a[num].siz=1;
		}
		else if(t==2)
		{
			scanf("%d%d%d%d",&x,&y,&xx,&yy);
			a[++num].x=x-1;a[num].y=y-1;a[num].val=1;a[num].id=num;a[num].xd=++cnt;a[num].siz=2;
			a[++num].x=xx;a[num].y=yy;a[num].val=1;a[num].id=num;a[num].xd=cnt;a[num].siz=2;
			a[++num].x=x-1;a[num].y=yy;a[num].val=-1;a[num].id=num;a[num].xd=cnt;a[num].siz=2;
			a[++num].x=xx;a[num].y=y-1;a[num].val=-1;a[num].id=num;a[num].xd=cnt;a[num].siz=2;
		}
		else
			break;
	}
	sort(a+1,a+num+1);	
	cdq(1,num);
	for(i=1;i<=cnt;i++)
		printf("%d\n",ans[i]);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值