hdu1892 See you(二维树状数组)

本文解析了 HDU 1892 题目的解题思路,采用二维树状数组实现矩阵操作,包括添加、删除、移动书籍及查询子矩阵书籍总数。介绍了树状数组的基本原理及其在频繁更新和查询场景中的高效应用。

题目连接: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是数组长度。

适用于 数组类型 频繁更新和查询的情况,是非常优秀的一种创新的"数据结构",但我还是无法证明它的正确性,再次膜拜大佬。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值