zoukankan      html  css  js  c++  java
  • 【模板篇】树状数组(五)

    好的 我们之前说过了树状数组的
    单点加区间查
    区间加单点查
    区间加区间查
    和没什么用的单点改查区间最值

    我们发现,这都是一维的树状数组,只能用来处理一维的东西..
    我们就想,为什么不能把树状数组放到平面上呢?
    然后就出现了二维树状数组

    大概就是长这个样子(大雾)

    int c[N][M];

    (如果要画出他表示的样子大概要一个3D立体图,可是这个地方太小,写不下= =)

    既然是树状数组,我们就不能忘记lowbit小朋友= =
    他现在长这个样子

    x&-x;

    (其实并没有什么改动)

    我们在二维中的单点指的就是坐标(x,y),区间指的就是以(x1,y1)为左上角(x2,y2)为右下角的矩形= =

    我们已经知道,树状数组维护的是前缀和,所以二维树状数组中每个点查到的就是以(1,1)为左上角,这个点为右下角的矩形的区间和= =
    所以我们单点加区间查可以这么写:

    void add(int x,int y,int s)
        for(int i=x;i<=n;i+=lowbit(i))
            for(int j=y;j<=m;j+=lowbit(j)) //其实就是多了一重循环
                c[i][j]+=s;
    int query(int x,int y){
        int s=0;
        for(int i=x;i;i-=lowbit(i))
            for(int j=y;j<=m;j+=lowbit(j))
                s+=c[i][j];
        return s;
    }
    //根据二维前缀和的性质,区间查是这样的
    int _query(int x1,int y1,int x2,int y2){
        return query(x2,y2)-(query,x1-1,y2)-query(x2,y1-1)+query(x1-1,y2-1);
    }

    *唯一要说明的是,循环不能再像一维的一样写

    for(;x;-=lowbit(x))
        for(;y;y-=lowbit(y))
            ...

    因为x跑之后几遍的时候,y是不应该被修改的..然后就会出现玄学的错误= =

    区间加单点查可以这么写:

    void add(int x,int y,int s)
        for(int i=x;i<=n;i+=lowbit(i))
            for(int j=y;j<=m;j+=lowbit(j))
                c[i][j]+=s;
    int query(int x,int y){
        int s=0;
        for(int i=x;i;i-=lowbit(i))
            for(int j=y;j<=m;j+=lowbit(j))
                s+=c[i][j];
        return s;
    }
    //二维差分
    void _add(int x1,int x2,int y1,int y2,int s){
        add(x1,y1,s);
        add(x1,y2+1,-s);
        add(x2+1,y1,-s);
        add(x2+1,y2+1,s);
    }

    先这样吧= =
    对于区间加单点查,贴一道例题(其实是因为做了这道题我才想到继续更树状数组的= =)
    这个昨天的CodeForces上的一道比D题水多了的E题= =
    http://codeforces.com/problemset/problem/869/E

    本来自己的思路是对的,结果自己觉得不行就没有写,后来发现是自己nc…(我还是太菜了QAQ)
    (虽然真正让我自己写还是会WA因为并没有意识到矩阵的编号会被卡OvO)
    其实这题完全是可做的(虽然最后因为map的问题交了n遍才过= =)

    题目大意(英文题,伤不起):
    给一个n*m(0<=m,n<=2500)的空地,支持三种操作:
    1. 在以(x1,y1)为左上角,(x2,y2)为右下角的矩形外侧建一堵墙..(不会盖到空地的最外围)
    2. 拆除以(x1,y1)为左上角,(x2,y2)为右下角的矩形外侧的墙..(墙保证存在)
    3. 查询(x1,y1)格与(x2,y2)格是否连通= =
    其中,墙围保证没有公共点..

    其实就是给每个矩阵编个号,1操作就把区间加这个编号,2操作就把这个区间减这个编号,3操作就查询这两个点的编号是否相等即可= =
    对于每堵墙的编号,我们开个map存一下即可..

    #include <map>
    #include <cstdio>
    #include <cstdlib>
    using namespace std;
    #define mp make_pair
    typedef long long ll;
    typedef pair<int,int> zb;
    map<pair<zb,zb>,int> mmp;
    inline int lb(int x){return x&-x;}
    inline ll myrand(){return (ll)rand()<<15|rand();}
    int n,m,q;
    ll c[2505][2505];
    inline int gn(){
        int a=0;char c=getchar();for(;c<'0'||c>'9';c=getchar());
        for(;c>='0'&&c<='9';c=getchar())a=(a<<1)+(a<<3)+c-'0';return a;
    }
    void _add(int x,int y,ll s){
        for(int i=x;i<=n;i+=lb(i))
            for(int j=y;j<=m;j+=lb(j))
                 c[i][j]+=s;
    }
    ll query(int x,int y){
        ll s=0;
        for(int i=x;i;i-=lb(i))
            for(int j=y;j;j-=lb(j))
                s+=c[i][j];
        return s;
    }
    void add(int x1,int y1,int x2,int y2,ll s){
        _add(x1,y1,s); _add(x1,y2+1,-s); _add(x2+1,y1,-s); _add(x2+1,y2+1,s);
    }
    int main(){
        srand(0x1204); //这个地方的种子大约是没有什么太大问题的..
        n=gn();m=gn();q=gn();
        while(q--){
            int opt=gn(),x1=gn(),y1=gn(),x2=gn(),y2=gn();
            switch(opt){
                case 1:{
                    ll k=myrand(); 
                    //这个地方不能简单的insert,因为可能会出现拆了又盖 盖了又拆的情况,如果不erase,
                    //后面的insert就会沦为无效操作= = 就会在第36个点WA...
                    mmp[mp(mp(x1,y1),mp(x2,y2))]=k;
    //              mmp.insert(mp(mp(mp(x1,y1),mp(x2,y2)),k));
    //              add(x1,y1,x2,y2,k);
                    break;
                }
                case 2:{
                    //如果上面选择了insert那么这里就要erase掉
    //              pair<zb,zb> pr=mp(mp(x1,y1),mp(x2,y2));
    //              int k=mmp[pr];
    //              mmp.erase(pr);
                    int k=mmp[mp(mp(x1,y1),mp(x2,y2))];
                    add(x1,y1,x2,y2,-k);
                    break;
                }
                default:{
                    ll q1=query(x1,y1),q2=query(x2,y2);
                    if(q1==q2) puts("Yes"); else puts("No");
                    break;
                }
            }
        }
    }

    哒哒哒~~

  • 相关阅读:
    gw经销商上传部分代码
    lib
    【转】sql server的随机函数newID()和RAND()
    【源码】仿qq记住登录信息
    关于ConfigurationManager类
    oracle 10 协议适配器错误解决办法
    配置对象数据源时,无法找到业务对象的解决办法
    private的由来,让能你很容易地区分private与protected的用法!
    大树扎根不稳,缘何不倒?
    Html服务器控件常用属性InnerHtml属性及InnerText属性的区别
  • 原文地址:https://www.cnblogs.com/enzymii/p/8412134.html
Copyright © 2011-2022 走看看