HDU 4312 Crowd
第一次用hash,第一次用二维树状数组,这道题所涉及到的知识点对我来说几乎是全新的。
于是乎这道题花去了我一晚上加一上午搞。
一开始直接开10001*10001的数组,维护N棵树状数组,结果显然爆内存了。不过现场赛的时候据说这样就能过,泥玛坑啊!~
后来搜题解,才知道。好吧,用过一维树状数组的童鞋学二维应该马上就能上手的,这里贴一个二维树状数组的资料。
二维树状数组资料:http://www.java3z.com/cwbwebhome/article/article1/1369.html?id=4804
至于hash这里用到的都是简单映射,看完hash的百度词条就能做这题了。
其实这道题的关键是在于坐标转换,转换完了直接就是二维树状数组的裸题了。
这道题我主要参考了沐阳的题解,大家也可以去看一下,两个参照着看效果可能好一点:http://www.cnblogs.com/Lyush/archive/2013/06/05/3119486.html
下面讲一下作为一个刚刚接触到这些知识点的人对于这道题的理解。
首先,在图上明显可以看出,以(x,y)为中心,曼哈顿距离为z的点构成了一个菱形,求的是菱形内所有点之和。
所以我们要想一个办法,把他们转换为矩形,这样就可以用二维树状做了。
这里就要用到坐标转换了,显然把当前的菱形旋转45度就是一个矩形了. 。
所以,我们把当前的方格网络映射到另一个矩形(以下称做映射矩阵)里去,转换公式是:nx = x-y+n; ny = x+y-1;
转换后的矩形大小是2N*2N,至于怎么推导公式,我暂时还不知到。大家可以去用这个公式将原图和映射图画出来,就能体会到这个公式的奇妙之处了。
原图是这样的,可以看到,标记的矩形有的行有三个点,有的行只有两个点,这使得二维树状数组无法使用。
然而转换公式有这样一个效果:
设原图中(x0,y0)映射到映射矩阵中的(x,y),转换公式使得原图不存在 和映射矩阵中(x-1,y)、(x+1,y)、(x,y-1)、(x,y+1) 相对应的点。
这样的效果使我们在求每一行的和的时候,可以在映射矩阵中直接按照(max(1,x-z) , min(n,x+z))的方法来求。
因为就算这一行只有两个点,而我们算了三个点,但是这多出来的一个点是0值。在原图中是没有任何值与之对应的。
然后直接就是二维树状数组的裸题。
但是这里要注意,我们显然开不了2N*2N的数组,而可能发生变化的数只有M*logn*logn个,大概是512万,不过根本不可能用到这么大,沐阳的代码400w就过了,后来我发先事实上400w还是卡着内存开的,我用的5000011就超内存了,所以我就把他的大素数抄过去用了。而之所以我的代码没有沐阳的快,那是因为他用了两个内联函数,这两个函数可能会被调用数十万次,这样子速度差就出来了,不过无所谓啦~
所以我们需要把这些二维数组里的数映射到一个一维数组里,直接找一个大素数来做hash映射就可以了。
下面上代码:
1 #include<stdio.h> 2 #include<iostream> 3 using namespace std; 4 5 #include<math.h> 6 #include<stdlib.h> 7 #include<algorithm> 8 #include<string> 9 #include<string.h> 10 #include<map> 11 #include<queue> 12 #define repA(p,q,i) for(int (i)=(p); (i)!=(q); ++(i) ) 13 #define repD(p,q,i) for(int (i)=(p); (i)!=(q); --(i) ) 14 #define repAE(p,q,i) for(int (i)=(p); (i)<=(q); ++(i) ) 15 #define repDE(p,q,i) for(int (i)=(p); (i)>=(q); --(i) ) 16 #define MOD 4073989 17 18 int n,m; 19 int p,x,y,z; 20 int LIM; 21 int set[MOD]; // 用来表示映射矩阵的值 22 int to[MOD]; // 用来做hash映射,表示坐标(x,y)映射到一维数组中的位置 23 // 在hash函数中采用的处理方法,使得可以不用邻接表去储存,其中i表示映射到的位置; 24 25 int lowbit( int i ); 26 void getxy(int x, int y, int &nx, int &ny); 27 int hash(int x, int y); 28 int search(int x, int y); 29 void update(int x, int y, int z); 30 long long sum(int x, int y); 31 32 33 int main() 34 { 35 while( scanf("%d",&n) != EOF ) 36 { 37 if(n==0) break; 38 LIM = (n<<1) ; 39 memset(to,0,sizeof(to) ); 40 memset(set,0,sizeof(set) ); 41 scanf("%d",&m); 42 int nx,ny,x1,xn,y1,yn; 43 while(m--) 44 { 45 scanf("%d%d%d%d",&p,&x,&y,&z); 46 getxy(x,y,nx,ny); 47 if(p == 1) 48 update(nx,ny,z); 49 else if(p == 2) 50 { 51 x1 = max(1, nx-z); 52 y1 = max(1, ny-z); 53 xn = min(LIM, nx+z); 54 yn = min(LIM, ny+z); 55 cout<<sum(xn,yn)-sum(xn,y1-1)-( sum(x1-1,yn)-sum(x1-1,y1-1) ) <<endl; 56 } 57 } 58 } 59 return 0; 60 } 61 62 int lowbit( int i ) 63 { 64 return i & (-i) ; 65 } 66 67 void getxy(int x, int y, int &nx, int &ny) 68 { 69 nx = x-y+n; 70 ny = x+y-1; 71 return ; 72 } 73 74 int hash(int x, int y) 75 { 76 int loc = x*LIM + y; 77 int key = loc % MOD; 78 for(int i=key; ; ++i) 79 { 80 if(i == MOD) i=0; 81 if( to[i] && to[i] != loc ) {} 82 else 83 { 84 to[i] = loc; 85 return i; 86 } 87 } 88 } 89 90 int search(int x, int y) 91 { 92 int loc = x*LIM + y; 93 int key = loc % MOD; 94 for(int i=key; ; ++i) 95 { 96 if(i == MOD) i=0; 97 if( to[i] && to[i] != loc ) {} 98 else 99 { 100 return i; 101 } 102 } 103 } 104 105 void update(int x, int y, int z) 106 { 107 for(int i=x; i<=LIM; i+=lowbit(i) ) 108 for(int j=y; j<=LIM; j+=lowbit(j) ) 109 set[ hash(i,j) ] += z; 110 return ; 111 } 112 113 long long sum(int x, int y) 114 { 115 long long all=0; 116 for(int i=x; i>0; i-=lowbit(i) ) 117 for(int j=y; j>0; j-=lowbit(j) ) 118 all += set[search(i,j) ]; 119 return all; 120 }