【模板篇】树状数组(五)

来源:互联网 发布:软件版权声明格式 编辑:程序博客网 时间:2024/06/03 20:53

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

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

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

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遍才过= =)

题目大意(英文题,伤不起):
给一个nm(0<=n,m<=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_pairtypedef 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;            }        }    }}

哒哒哒~~

原创粉丝点击