树状数组的作用:
用nlog(n)完成预处理,每次log(n)的效率完成对每次1项到k项求和的询问。
树形数组里最关键的建图依据是lowbit即该数最右边的1所对应的的数值。
每个数的lowbit的计算方法是n & (-n)
原理是因为计算机中区负数的原理是把各个位取反之后加1。
之后进行与运算即可得到lowbit。
每棵树的深度是由lowbit的大小决定,lowbit最大的数为整个树的根。
lowbit为1的数为页。
之后用数组c记录a[i - lowbit(i) + 1], a[i - lowbit(i) + 2] , a[i - lowbit(i) + 3]..... a[i ]的值。
每次查询时只要求出c[i] +c[i - lowbit(i)] + c[i - 2 * lowbit(i)] + .......直到i - k * lowbit(i)小于等于0即可算出。
(画出图来原理一目了然)
每次更改只要改变i,i + lowbit(i) 。。。。。直到i + k * lowbit(i)大于等于数组大小即可。
(原因是只有下标为这些数的时候求和时才有这个数参与,画图之后很容易验证)。
POJ2029
比较水的一道查询题。题意是给出一个图上一些坐标,问用一个小正方形最多能圈几个点。
我的思路就是每次找一个顶点查找里面点的个数,找出含有点最多的点输出。
算点数目的时候每行用树状数组统计每一行之后把各行相加得到总数。
(实际算点数目这里完全可以用递推来解决,但是因为处理图时比较乱,老是关系找不好,
边界找不准,WA了好多次,下次这种题一定要边画图边编码。这道题不改了,代码已经很乱了测试数据也太小了)
#include<iostream>
#include<Cstdio>
using namespace std;
int map[200][200];
int cmap[200][200], N, W, H;
int res[200][200];
int S, T;
int lowBit(int n)
{
return (n & (-n));
}
void f()
{
int temp, i, j, k;
for(i = 1; i <= H; i++)
{
for(j = 1; j <= W; j++)
{
temp = j - lowBit(j);
for(k = temp + 1; k <= j; k++)
{
cmap[j][i] += map[k][i];
}
}
}
}
int sum(int x, int n)
{
if(x <= 0)
return 0;
int ret = 0;
int temp, i, j, k;
while(x > 0)
{
ret += cmap[x][n]; x -= lowBit(x);
//cout << x << endl;
}
return ret;
}
int getSum(int x, int y)
{
int q = 0;
int temp, i, j, k;
for(i = y; i < y + T; i++)
{
q += sum(x + S-1, i) - sum(x - 1, i);
}
return q;
}
void result()
{
int max = -1;
int temp, i, j, k;
for(i = 1; i <= W - S + 1; i++)
{
for(j = 1; j <= H - T + 1; j++)
{
res[i][j] = getSum(i, j);
if(res[i][j] > max)
max = res[i][j];
}
}
cout << max << endl;
}
int main()
{
int x, y;
int temp, i, j, k;
while(1)
{
scanf("%d", &N);
if(N == 0)
break;
scanf("%d%d", &W, &H);
for(i = 0; i <= W; i++)
{
for(j =0; j <= H; j++)
{
map[i][j] = 0;
cmap[i][j] = 0;
}
}
//cout << N << endl;
while(N--)
{
scanf("%d%d", &x, &y);
map[x][y] = 1;
}
scanf("%d%d", &S, &T);
f();
result();
}
return 0;
}