HDU-4456 Crowd 二维树状数组+坐标转换
題意:給定一個N*N的網格,現在M組操作,一種操作時改變網格上的某個單點的權值,另外一種操作是求到一點曼哈頓距離為小于等于k的所有的權值和,初始化網格所有點的權值為0。
解法:這題如果沒有那些特定的條件,那么就是一個純凈的二維樹狀數組。對于題目中的要求需要解決的兩個問題是:如何將題目中的曼哈頓距離轉化為規則的矩形,以及如何避免開辟一個巨大的數組。
1.如何轉化為矩形問題,這里處理的方法就是把所有的點都旋轉45度,需要一個2N*2N的數組數組才能容下坐標轉換之后的圖,坐標的轉化方式為nx = x - y + N, ?ny = x + y - 1。可以這樣看,轉化之后的同行相鄰兩列的坐標差為(-1, 1), 相鄰兩列相鄰兩行的坐標差為(1, 1),所以如果以(0, 0)為參考點(即該點轉化前后位置不發生變化)的那么(x, y)就變為(x-y, x+y),又因為所有的點都應該在這個2N*2N的矩形中,因此我們給(1,1)這個點衡坐標加上N,縱坐標-1是為了讓最靠左邊的點的列從1開始,這樣轉化后(1,1)->(N,1), (1,N)->(1, N), (N, 1)->(2N-1, N), (N, N)->(N, 2N-1)。
2.如何避免開一個大的數組,由于轉化之后的數組達到了20000*20000,這個數組我memset后電腦就死機了,神奇的是編譯的時候并沒有提示該數組過大。一種有效的方法是將二維的坐標hash處理,即找一個大的質數,每次的更新都選擇一個合適的一維數組中的數來表示一個二維中的數,有一個地方要特別注意就是查詢的時候并不需要hash處理,如果沒有某個數對應的hash值話直接就是0了。
感謝 HDU?xiaotao的指點,分析了代碼后才恍悟此題的精妙。HDU-4312同樣存在這樣的一個坐標的轉化過程,有興趣的可以去做做。
代碼如下:
#include <cstdlib> #include <cstring> #include <cstdio> #include <algorithm> #include <iostream> #include <map> using namespace std;const int MOD = 4073989; int N, M, LIM; int to[MOD]; int bit[MOD];int hash(int x) {int key = x % MOD;for (int i = key; ; i++) {if (i == MOD) i = 0;if (to[i] && to[i] != x) {}else {to[i] = x;return i;}} }int search(int x) {int key = x % MOD;for (int i = key; ; i++) {if (i == MOD) i = 0;if (to[i] && to[i] != x) {}else {// 查詢并不需要給出一個離散化之后的值 return i;}} }inline int lowbit(int x) {return x & (-x); }void add(int x, int y, int val) {for (int i = x; i <= LIM; i += lowbit(i)){for (int j = y; j <= LIM; j += lowbit(j)) {bit[hash(i*LIM+j)] += val;}} }inline void get(int x, int y, int &rx, int &ry) {rx = x - y + N;ry = x + y - 1; }int sum(int x, int y) {int ret = 0;for (int i = x; i; i -= lowbit(i)) {for (int j = y; j; j -= lowbit(j)) {ret += bit[search(i*LIM+j)];}}return ret; }int main() {while (scanf("%d", &N), N) {scanf("%d", &M);LIM = N << 1;memset(to, 0, sizeof (to));memset(bit, 0, sizeof (bit));int x, y, z, p;int rx, ry, x1, y1, x2, y2;for (int i = 0; i < M; ++i) {scanf("%d%d%d%d", &p, &x, &y, &z);get(x, y, rx, ry);if (p == 1) {add(rx, ry, z);} else {x1 = max(1, rx-z);y1 = max(1, ry-z);x2 = min(LIM, rx+z);y2 = min(LIM, ry+z);printf("%d\n", sum(x2, y2)-sum(x1-1, y2)-sum(x2, y1-1)+sum(x1-1,y1-1));}}}return 0; }?
轉載于:https://www.cnblogs.com/Lyush/archive/2013/06/05/3119486.html
總結
以上是生活随笔為你收集整理的HDU-4456 Crowd 二维树状数组+坐标转换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTML去掉列表前面的符号!
- 下一篇: [原创] Robot framework