前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【 HDU - 4456 】Crowd (二维树状数组、cdq分治)

【 HDU - 4456 】Crowd (二维树状数组、cdq分治)

作者头像
饶文津
发布2020-06-02 14:02:20
3170
发布2020-06-02 14:02:20
举报
文章被收录于专栏:饶文津的专栏饶文津的专栏

BUPT2017 wintertraining(15) #5A

HDU 4456

题意

给你一个n行n列的格子,一开始每个格子值都是0。有M个操作,p=1为第一种操作,给格子(x,y)增加z。p=2为询问与格子(x,y)的曼哈顿距离不超过z的格子值的和。

(1 ≤ n ≤10 000, 1 ≤ m ≤ 80 000)

题解

这道题如果数据不大,那就可以直接用二维的树状数组来做。

方法1:二维树状数组

方法2:cdq分治+树状数组

另外这题和一道经典的cdq分治相似,以下是别人写的题解:

BZOJ 2683 简单题 cdq分治+树状数组 -Claude - 博客频道 - CSDN.NET

只要把方法1中的坐标转换加上就好了。

我自己总结一下就是,将操作按坐标的x为第一关键字,y为第二关键字,操作种类为第三关键字排序。然后将所有操作进行cdq分治。solve(l,r)就是解决时序在l到r的操作,mid=(l+r)/2,时序小于mid的1操作 对 时序大于mid 且 排在它后面的2操作有影响,因此一次循环就可以完成左区间的修改和右区间的累加答案。我们用树状数组sum[i]表示y在[1,i]的格子之和。每次solve时sum都清空一次,也就是只计算x不超过q[mid].x的格子。将时序按不超过mid和超过mid分别放在q的左右两边,再分治处理左右子区间。具体看代码。

代码

方法1
代码语言:javascript
复制
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 80005
#define M 4000000
using namespace std;
int n,m,sum[M];
int p[N],qx[N],qy[N],z[N],h[M],cnt;
void add(int x,int y,int z){
	for(;x<=n;x+=x&(-x))
	for(int j=y;j<=n;j+=j&(-j))
		h[cnt++]=x*n+j;
}
void update(int x,int y,int z){
	for(;x<=n;x+=x&(-x))
	for(int j=y;j<=n;j+=j&(-j)){
		int pos=lower_bound(h, h+cnt, x*n+j)-h;
		sum[pos]+=z;
	}
}
int getsum(int x,int y){
	int ans=0;
	for(;x;x-=x&(-x))
	for(int j=y;j;j-=j&(-j)){
		int pos=lower_bound(h, h+cnt, x*n+j)-h;
		if(x*n+j==h[pos])ans+=sum[pos];
	}
	return ans;
}
int main() {
	while(scanf("%d",&n),n){
		n*=2;cnt=0;
		memset(sum,0,sizeof sum);
		scanf("%d",&m);
		for(int i=1,x,y;i<=m;i++){
			scanf("%d%d%d%d",&p[i],&x,&y,&z[i]);
			qx[i]=x-y+n/2,qy[i]=x+y;
			if(p[i]==1)add(qx[i],qy[i],z[i]);
		}
		sort(h,h+cnt);
		for(int i=1;i<=m;i++){
			if(p[i]==1)update(qx[i],qy[i],z[i]);
			else{
				int lx=max(1,qx[i]-z[i])-1,rx=min(qx[i]+z[i],n);
				int ly=max(1,qy[i]-z[i])-1,ry=min(qy[i]+z[i],n);
				printf("%d\n",getsum(rx,ry)-getsum(lx,ry)-getsum(rx,ly)+getsum(lx,ly));
			}
		}
	}	
	return 0;
}
方法2
代码语言:javascript
复制
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define M 80005
#define N 20005
using namespace std;
struct Query{
	int p,x,y,z,i,t;
}q[M<<2],tq[M<<2];
bool cmp(Query a,Query b){
	if(a.x==b.x){
		if(a.y==b.y)return a.p<b.p;
		return a.y<b.y;
	}
	return a.x<b.x;
}
int sum[N],n2;
void add(int x,int v){
	for(;x<=n2;x+=x&(-x))sum[x]+=v;
}
int getsum(int x){
	int ans=0;
	for(;x;x-=x&(-x))ans+=sum[x];
	return ans;
}
int ans[M],cnt;
void solve(int l,int r){
	if(l==r)return;
	int mid=l+r>>1;
	for(int i=l;i<=r;i++)
		if(q[i].p==1&&q[i].t<=mid)
			add(q[i].y,q[i].z);
		else if(q[i].p==2&&q[i].t>mid)
			ans[q[i].i]+=q[i].z*getsum(q[i].y);
	for(int i=l;i<=r;i++)if(q[i].p==1&&q[i].t<=mid)
		add(q[i].y,-q[i].z);
	int l1=l-1,l2=mid;
	for(int i=l;i<=r;i++)
		if(q[i].t<=mid)tq[++l1]=q[i];
		else tq[++l2]=q[i];
	for(int i=l;i<=r;i++)q[i]=tq[i];
	solve(l,mid);solve(mid+1,r);
}
int n,m;
int main() {
	while(scanf("%d",&n),n){
		scanf("%d",&m);
		int t=0;cnt=0;n2=n*2;
		memset(ans,0,sizeof ans);
		for(int i=1,p,x,y,z;i<=m;i++){
			scanf("%d%d%d%d",&p,&x,&y,&z);
			if(p==1){
				cnt++;
				q[cnt]=(Query){p,x+y,x-y+n,z,0,cnt};
			}else{
				t++;
				int xx=x+y,yy=x-y+n;
				int x1=max(xx-z-1,1),y1=max(yy-z-1,1);
				int x2=min(xx+z,n2),y2=min(yy+z,n2);
				cnt++;
				q[cnt]=(Query){p,x1,y1,1,t,cnt};
				cnt++;
				q[cnt]=(Query){p,x2,y2,1,t,cnt};
				cnt++;
				q[cnt]=(Query){p,x2,y1,-1,t,cnt};
				cnt++;
				q[cnt]=(Query){p,x1,y2,-1,t,cnt};
			}
		}
		sort(q+1,q+1+cnt,cmp);
		solve(1,cnt);
		for(int i=1;i<=t;i++)printf("%d\n",ans[i]);
	}
	return 0;
}
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-02-26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题意
  • 题解
    • 方法1:二维树状数组
      • 方法2:cdq分治+树状数组
      • 代码
        • 方法1
          • 方法2
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档