题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1892
1.题意:
一个矩阵中含有1000*1000个格子,每个格子放了一本书,现在有4种操作:
①'A' 在某个格子添加n本书; ②'D' 在某个格子删除n本书;③'M' 从某个格子移动n本书到另一个格子;
④查询以 x1,y1 x2,y2 为对角线端点的子矩阵中,总共含有多少本书。
2.思路:二维树状数组
树状数组介绍: https://blog.youkuaiyun.com/zzti_xiaowei/article/details/81053094
此题还要注意:
①判断x1,y1 x2,y2 哪个节点靠左。
②注意阅读题目,假如操作的格子不够n本书,则取其所有的书。
③题目是多个case,为了避免每个 case开头都要对数组的初始化,我用set记录了上一次case中修改的那些节点,然后只需要将这些节点更新为1即可。
3.代码:
#include<stdio.h>
#include<set>
using namespace std;
int n,m;
int lowbit(int x){
return x & (-x);
}
int arr[1000+5][1000+5];
int cc[1000+5][1000+5];
void update(int x,int y,int add)
{
for(int i=x;i<=n;i+=lowbit(i)){
for(int j=y;j<=m;j+=lowbit(j)){
cc[i][j] += add;
}
}
}
int getSum(int x,int y){
int rs = 0;
for(int i=x;i>0;i-=lowbit(i)){
for(int j=y;j>0;j-=lowbit(j)){
rs += cc[i][j];
}
}
return rs;
}
int max(int a,int b){
return a>b?a:b;
}
int min(int a,int b){
return a<b?a:b;
}
struct Node
{
int x,y;
Node(int xx,int yy){
x = xx ; y = yy;
}
};
bool operator < (Node a,Node b){
if(a.x==b.x && a.y==b.y) return false;
return true;
}
int main(){
m=n=1001;
int cas;
scanf("%d",&cas);
int x1,x2,y1,y2;
int x,y;
int n1;
for(int j=1;j<=n;j++){
for(int k=1;k<=m;k++){
arr[j][k] = 1;
update(j,k,1);
}
}
set<Node> s ;
for(int i=1;i<=cas;i++){
set<Node>::iterator it = s.begin();
while(it!=s.end()){ //只更新上一次case被修改的节点,避免超时TLE
Node tmp = (*it);
int now = arr[tmp.x][tmp.y];
if(now!=1){
arr[tmp.x][tmp.y] = 1;
update(tmp.x,tmp.y,1-now);
}
it++;
}
s.clear();
printf("Case %d:\n",i);
int qs;
scanf("%d",&qs);
int rs;
for(int j=1;j<=qs;j++){
getchar();
char op;
scanf("%c",&op);
switch(op){
case 'S':
scanf("%d",&x1);scanf("%d",&y1);scanf("%d",&x2);scanf("%d",&y2);
x1++; y1++; x2++; y2++;
if(x1>x2){
int tmp = x2;
x2 = x1;
x1 = tmp;
}
if(y1>y2){
int tmp = y1;
y1 = y2;
y2 = tmp;
}
rs = getSum(x2,y2) - getSum(x2,y1-1) - getSum(x1-1,y2) + getSum(x1-1,y1-1);
printf("%d\n",rs);
break;
case 'A':
scanf("%d",&x);scanf("%d",&y);scanf("%d",&n1);
x++;y++;
s.insert(Node(x,y)); //记录这个被更新的节点
update(x,y,n1);
arr[x][y] += n1;
break;
case 'D':
scanf("%d",&x);scanf("%d",&y);scanf("%d",&n1);
x++;y++;
s.insert(Node(x,y));//记录这个被更新的节点
n1 = min(n1,arr[x][y]);
update(x,y,-n1);
arr[x][y] -= n1;
break;
case 'M' :
scanf("%d",&x1);scanf("%d",&y1);scanf("%d",&x2);scanf("%d",&y2);
scanf("%d",&n1);
x1++;y1++;x2++;y2++;
s.insert(Node(x1,y1));//记录这个被更新的节点
s.insert(Node(x2,y2));//记录这个被更新的节点
n1 = min(n1,arr[x1][y1]);
update(x1,y1,-n1);
update(x2,y2,n1);
arr[x1][y1] -= n1;
arr[x2][y2] += n1;
break;
}
}
}
return 0;
}//ac c++ 405MS
总结:树状数组 就是利用二进制的特性 去统计前n项和。不论是修改还是查询操作都是O(logN) 的操作,此处N是数组长度。
适用于 数组类型 频繁更新和查询的情况,是非常优秀的一种创新的"数据结构",但我还是无法证明它的正确性,再次膜拜大佬。
本文解析了 HDU 1892 题目的解题思路,采用二维树状数组实现矩阵操作,包括添加、删除、移动书籍及查询子矩阵书籍总数。介绍了树状数组的基本原理及其在频繁更新和查询场景中的高效应用。
258

被折叠的 条评论
为什么被折叠?



