• 【 HDU


    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:二维树状数组

    因为数据比较大,所以要离线处理并且离散化一下修改的值,再用二维树状数组:

    查询的是菱形,我们把坐标用(x-y+n,x+y)来表示,就相当于查询的是矩形了,也就是将坐标轴旋转45°,并平移。

    每次修改的坐标(x,y)以((xcdot ncdot 2+y))存在h数组中,假设共cnt次修改。给h排个序,再依次处理m个操作。每次修改需要用(O(log^2(ncdot 2)cdot log(cnt)))的时间,(pos=lower\_bound(h,h+cnt,icdot ncdot 2+j)-h),再更新树状数组sum的pos位置的值。每次查询用容斥定理计算一下,树状数组求和过程的i,j位置在h中有对应的值,就把sum[pos]加到ans中。

    但是这个h数组和sum数组我觉得开到4000000应该不够的,我认为应该最坏有80000个修改,每个修改(log^2(ncdot 2))次cnt++,n*2最多是20000,那大概需要80000*15*15=18000000,但是这么大就MLE了,8000000也是MLE。

    方法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

    #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
    ",getsum(rx,ry)-getsum(lx,ry)-getsum(rx,ly)+getsum(lx,ly));
    			}
    		}
    	}	
    	return 0;
    }
    

    方法2

    #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
    ",ans[i]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    三目运算符
    程序流程结构——if语句
    类型转换
    运算符
    scanf函数与getchar函数
    结构体成员有冒号 位域 位段
    AI作曲的一个点子
    《惯性导航》邓正隆 第一章 惯性导航的基础知识
    《C陷阱与缺陷》 第0章导读 第1章词法陷阱
    类与类之间的关系 18
  • 原文地址:https://www.cnblogs.com/flipped/p/6443481.html
Copyright © 2020-2023  润新知