Background 背景
SuperBrother在机房里闲着没事干(再对比一下他的NOIP,真是讽刺啊……),于是便无聊地开始玩“打鼹鼠”……
Description 描述
在这个“打鼹鼠”的游戏中,鼹鼠会不时地从洞中钻出来,不过不会从洞口钻进去(鼹鼠真胆大……)。洞口都在一个大小为 n(n≤1024) 的正方形中。这个正方形在一个平面直角坐标系中,左下角为 (0,0) ,右上角为 (n−1,n−1) 。洞口所在的位置都是整点,就是横纵坐标都为整数的点。而SuperBrother也不时地会想知道某一个范围的鼹鼠总数。这就是你的任务。
Input 输入
每个输入文件有多行。
第一行,一个数 n ,表示鼹鼠的范围。
以后每一行开头都有一个数 m ,表示不同的操作:
m=1 ,那么后面跟着3个数 x,y,k(0≤x,y<n) ,表示在点 (x,y) 处新出现了 k 只鼹鼠;
m=2 ,那么后面跟着 4 个数 x1,y1,x2,y2(0≤x1≤x2<n,0≤y1≤y2<n) ,表示询问矩形 (x1,y1)−(x2,y2) 内的鼹鼠数量;
m=3 ,表示老师来了,不能玩了。保证这个数会在输入的最后一行。
询问数不会超过 10000 ,鼹鼠数不会超过maxlongint。
//maxlongint=INT_MAX
Output 输出
对于每个 m=2 ,输出一行数,这行数只有一个数,即所询问的区域内鼹鼠的个数。
Sample Input 样例输入
4
1 2 2 5
2 0 0 2 3
3
Sample Output 样例输出
5
Limits 限制
1s 128MB
这道题刚看起来像是线段树,只不过要维护的东西不是一维数轴上的点,而是二维平面直角坐标系中的有序数对。(学过数学的)我们都知道,平面直角坐标系由两个互相垂直的数轴构成,所以在一维线段树下再维护一个线段树(类似于树套树),即可搞定。凭着初生牛犊不怕虎(其实是敬业)的精神,耗了 4h+ 搞定这个(鬼畜的)二维线段树
#include
#include
#define MAXN 1025
using namespace std;
struct node
{
int y_left,y_right;
int middle;
int sum;
};
struct node1
{
int x_left,x_right;
int middle;
node sub[2*MAXN];
};
node1 tree[2*MAXN];
int n,m;
int x1,x2,y1,y2,k;
//建树思路:建一维,再建一维
//这回线段树是左闭右闭,因为左闭右开的线段树写着写着脑抽了......
//其实左闭右闭也挺好写,记着就好了......
void Build_Sub_Tree(int x_id,int y_id,int y_l,int y_r) //第二维建树
{
tree[x_id].sub[y_id].y_left=y_l;
tree[x_id].sub[y_id].y_right=y_r;
if(y_l==y_r) return ;
int mid=(y_l+y_r)>>1;
tree[x_id].sub[y_id].middle=mid;
Build_Sub_Tree(x_id,y_id<<1,y_l,mid);
Build_Sub_Tree(x_id,y_id<<1|1,mid+1,y_r);
}
void Build_A_Tree(int x_id,int x_l,int x_r,int y_l,int y_r)//第一维建树
{
tree[x_id].x_left=x_l;
tree[x_id].x_right=x_r;
Build_Sub_Tree(x_id,1,y_l,y_r);
if(x_l==x_r)return ;
int mid=(x_l+x_r)>>1;
tree[x_id].middle=mid;
Build_A_Tree(x_id<<1,x_l,mid,y_l,y_r);
Build_A_Tree(x_id<<1|1,mid+1,x_r,y_l,y_r);
}
//改值思路:改一维,再改一维
void modify_y(int x_id,int y_id,int y,int k)//第二维修改
{
tree[x_id].sub[y_id].sum+=k;//一个位置可能出现多次鼹鼠
if(tree[x_id].sub[y_id].y_left==tree[x_id].sub[y_id].y_right)
return;
if (y<=tree[x_id].sub[y_id].middle)
modify_y(x_id,y_id<<1,y,k);
else modify_y(x_id,y_id<<1|1,y,k);
tree[x_id].sub[y_id].sum=tree[x_id].sub[y_id<<1].sum+tree[x_id].sub[y_id<<1|1].sum;
}
void modify_x(int x_id,int x,int y,int k)//第一维修改
{
modify_y(x_id,1,y,k);
if (tree[x_id].x_left==tree[x_id].x_right)
return;
if (x<=tree[x_id].middle) modify_x(x_id<<1,x,y,k);
else modify_x(x_id<<1|1,x,y,k);
}
//查询思路:查一维,再查一维
int query_y(int x_id,int y_id,int y1,int y2)//查找第二维
{
if(tree[x_id].sub[y_id].y_left==y1&&tree[x_id].sub[y_id].y_right==y2)
return tree[x_id].sub[y_id].sum;
if (tree[x_id].sub[y_id].middle>=y2)
return query_y(x_id,y_id<<1,y1,y2);
else if (tree[x_id].sub[y_id].middlereturn query_y(x_id,y_id<<1|1,y1,y2);
else return query_y(x_id,y_id<<1,y1,tree[x_id].sub[y_id].middle)+query_y(x_id,y_id<<1|1,tree[x_id].sub[y_id].middle+1,y2);
}
int query_x(int x_id,int x1,int y1,int x2,int y2) //查找第一维
{
if (tree[x_id].x_left==x1&&tree[x_id].x_right==x2)return query_y(x_id,1,y1,y2);
if (tree[x_id].middle>=x2) return query_x(x_id<<1,x1,y1,x2,y2);
else if (tree[x_id].middlereturn query_x(x_id<<1|1,x1,y1,x2,y2);
else return query_x(x_id<<1,x1,y1,tree[x_id].middle,y2)+query_x(x_id<<1|1,tree[x_id].middle+1,y1,x2,y2);
}
int main()
{
scanf("%d",&n);
Build_A_Tree(1,1,n+1,1,n+1);
while(scanf("%d",&m)!=EOF)
{
if (m==1)
{
scanf("%d %d %d",&x1,&y1,&k);
modify_x(1,x1+1,y1+1,k);
}
else if (m==2)
{
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
if (x1>x2) swap(x1,x2);//查询一定注意这里!!!!
if (y1>y2) swap(y1,y2);
printf("%d\n",query_x(1,x1+1,y1+1,x2+1,y2+1));
}
else break;
}
return 0;
}
大家是不是看吐了?
这个代码在VJ上 370ms , 66320KB ,显然在代码实现和时空复杂度上还是不够好(我要 0ms ……)
于是,一个神奇的东西出现了,树状数组
其实,本题是树状数组的一道大水题!!!!!
大家比较一下(请大家不要过于激动……)
#include
#define MAXN 1200
using namespace std;
int c[MAXN][MAXN];
int n,m;
int x1,x2,y1,y2;
int k;
int lowbit(int x)//lowbit操作是降低复杂的的关键
{
return x&(-x);
}
int sum(int x,int y)
{
int ret=0;
for (int i=y;i;i-=lowbit(i))
for (int j=x;j;j-=lowbit(j))
ret+=c[i][j];
return ret;
}
void add(int x,int y,int delta)
{
for (int i=y;i<=n;i+=lowbit(i))
for (int j=x;j<=n;j+=lowbit(j))
c[i][j]+=delta;
}
int main()
{
scanf("%d",&n);
while (scanf("%d",&m)!=EOF)
{
if (m==1)
{
scanf("%d %d %d",&x1,&y1,&k);
add(x1+1,y1+1,k);
}
else if (m==2)
{
scanf("%d %d %d %d",&x1,&y1,&x2,&y2);
printf("%d\n",sum(x2+1,y2+1)-sum(x1,y2+1)-sum(x2+1,y1)+sum(x1,y1));
//小小的运用了一下面积的割补法,题中区域内鼹鼠个数包括边界上的,切记
}
else break;
}
return 0;
}
VJ上 60ms , 6140KB ……
是不是突然觉得二维线段树白写了……
关于树状数组的介绍,由于篇幅有限请自行度娘
关于二维线段树是不是白写了,额……,本人不置可否
//本人观点:在这道题上是,但是说不定哪天就碰上一道写二维线段树的题呢