好的 我们之前说过了树状数组的
单点加区间查
区间加单点查
区间加区间查
和没什么用的单点改查区间最值
我们发现,这都是一维的树状数组,只能用来处理一维的东西..
我们就想,为什么不能把树状数组放到平面上呢?
然后就出现了二维树状数组
大概就是长这个样子(大雾)
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;
}
}
}
}
哒哒哒~~