多维树状数组
hdu2642
http://acm.hdu.edu.cn/showproblem.php?pid=2642
题目大意:
现在假设天空是一个二维平面(1000*1000)。坐标从(0,0)点开始。现在给出N条信息。
"B X Y",B为字符,x、y是整数,表示将坐标(x,y)的星星变亮。
"D XY",D为字符,x、y是整数,表示将坐标(x,y)的星星变暗。
"Q X1 X2 Y1 Y2",Q是字符,X1、X2、Y1、Y2是整数,表示问有多少颗星星在X1、X2、
Y1、Y2确定的矩形中。
思路:
建立一个二维的树状数组,单点更新,区间求值。其实和一维的差不多。只不过第i个元素变
为了第x行第y列的元素。更新和求值都是二维维护数组数组。再用一个二维数组Mark[][]表
示(x、y)处的星星的明暗情况。因为坐标是从(0,0)点开始的,而Lowbit(0) = 0,所以,另
所有的横纵坐标都加1,使坐标从(1,1)点开始。
#include<iostream> #include<cstring> #include<cstdio> #include<algorithm> using namespace std; const int M=1e3+3; int a[M][M],vis[M][M]; int lowbit(int x){ return x&(-x); } void update(int x,int y,int c){ for(int i=x;i<M;i+=lowbit(i)) for(int j=y;j<M;j+=lowbit(j)) a[i][j]+=c; } int sum(int x,int y){ int ans=0; for(int i=x;i>0;i-=lowbit(i)) for(int j=y;j>0;j-=lowbit(j)) ans+=a[i][j]; return ans; } char s[2]; int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%s",s); if(s[0]=='Q'){ int x1,x2,y1,y2; scanf("%d%d%d%d",&x1,&x2,&y1,&y2); x1++,x2++,y1++,y2++; if(x1<x2) swap(x1,x2); if(y1<y2) swap(y1,y2); printf("%d\n",sum(x1,y1)+sum(x2-1,y2-1)-sum(x1,y2-1)-sum(x2-1,y1)); } else{ int x,y; scanf("%d%d",&x,&y); x++,y++; if(s[0]=='B'){ if(!vis[x][y]){ vis[x][y]=1; update(x,y,1); } } else{ if(vis[x][y]){ vis[x][y]=0; update(x,y,-1); } } } } return 0; }
三维数状数组修改板子:
if(x==1) { int x1,y1,z1,x2,y2,z2,w; cin>>x1>>y1>>z1>>x2>>y2>>z2>>w; update(x1,y1,z1,w); update(x1,y2+1,z1,-w); update(x2+1,y1,z1,-w); update(x2+1,y2+1,z1,w); update(x1,y1,z2+1,-w); update(x1,y2+1,z2+1,w); update(x2+1,y1,z2+1,w); update(x2+1,y2+1,z2+1,-w); } if(x==2) { int x,y,z; cin>>x>>y>>z; cout<<sum(x,y,z)<<endl; }
hdu
http://acm.hdu.edu.cn/showproblem.php?pid=3584
思路:
三维树状数组区间更新、单点查询。更新区间(a,b)时,在 a 和 b+1 处都加1,前边表示增加1,后边是
抵消加1操作,最后询问时,用Query(x) % 2 即为单点的值(0或1)。三维区间更新,要注意区间的合并会
重复,所以要利用容斥原理,更新区间(x1,y1,z1)~(x2,y2,z2)时,需要利用容斥原理。更新如下:
Update(x1,y1,z1); Update(x1,y1,z2+1); Update(x1,y2+1,z1); Update(x1,y2+1,z2+1);
Update(x2+1,y1,z1); Update(x2+1,y1,z2+1); Update(x2+1,y2+1,z1); Update(x2+1,y2+1,z2+1);
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int M=110; int a[M][M][M]; int n,m; int lowbit(int x){ return x&(-x); } void update(int x,int y,int z){ for(int i=x;i<=n;i+=lowbit(i)) for(int j=y;j<=n;j+=lowbit(j)) for(int k=z;k<=n;k+=lowbit(k)) a[i][j][k]++; } int sum(int x,int y,int z){ int ans=0; for(int i=x;i>0;i-=lowbit(i)) for(int j=y;j>0;j-=lowbit(j)) for(int k=z;k>0;k-=lowbit(k)) ans+=a[i][j][k]; return ans; } int main(){ while(~scanf("%d%d",&n,&m)){ memset(a,0,sizeof(a)); while(m--){ int op; scanf("%d",&op); int x,y,z,xx,yy,zz; if(op==1){ scanf("%d%d%d%d%d%d",&x,&y,&z,&xx,&yy,&zz); update(x,y,z); update(xx+1,y,z); update(x,yy+1,z); update(x,y,zz+1); update(xx+1,yy+1,z); update(xx+1,y,zz+1); update(x,yy+1,zz+1); update(xx+1,yy+1,zz+1); } else{ scanf("%d%d%d",&x,&y,&z); printf("%d\n",sum(x,y,z)%2); } } } return 0; }
http://poj.org/problem?id=2299
题意:给序列,只能相邻交换,问交换次数最小数;
分析:1、问题转换为逆序对数
2、题目给定的序列数太大,离散化;
3、考虑用树状数组
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int M=5e5+5; int cnt[M],z[M],a[M]; int n; int lowbit(int x){ return x&(-x); } void update(int x){ while(x<=n) cnt[x]++,x+=lowbit(x); } int sum(int x){ int ans=0; while(x) ans+=cnt[x],x-=lowbit(x); return ans; } int main(){ while(~scanf("%d",&n)&&n){ memset(cnt,0,sizeof(cnt)); for(int i=0;i<n;i++) scanf("%d",&a[i]),z[i]=a[i]; sort(z,z+n); ll ans=0; for(int i=0;i<n;i++) a[i]=lower_bound(z,z+n,a[i])-z+1; /* for(int i=0;i<n;i++) cout<<a[i]<<" "; cout<<endl;*/ for(int i=0;i<n;i++){ update(a[i]); ans+=i+1-sum(a[i]); } printf("%I64d\n",ans); } return 0; }