树状数组与二维树状数组

树状数组:

C[i]数组的理解:
图一:

如何计算C数组?(数组下标用二进制表示,尾部有n个0,c数组就等于n+1个相加)

  • 二进制末尾为1,则为本身,如c[1],c[11],c[101];
  • 若末尾有0,则依次将末尾的0依次变为1,并累加起来就是c数组,如c[10100]=c[10010]+c[10011]+a[10100];(即10100的中的00依次变为1,得10和11,然后加上前的101-1=100,得10010,和10011.
    c[1]:c[1]=c[1]
    c[2]:c[10]=c[1]+a[10];
    c[3]:c[11]=a[11];
    c[4]:c[100]=c[10]+c[11]+a[100];
    c[5]:c[101]=a[101];
    c[6]:c[110]=c[101]+a[110];
    c[7]:c[111]=a[111];
    c[8]:c[1000]=c[100]+c[110]+c[111]+a[1000];

如何计算一个数de 尾部有多少个0
根据补码性质有:x^(-x)
或者:x&(x^(x-1))
注意这里返回的是二进制,如x=100(二进制),返回的是100,值是4,因为后续就是用的这个,再如x=10100(二进制)返回的是100,值是4.。

代码

int lowbit(int x)
{
	return x^(-x);
	//return x&(x^(x-1));
}


初始输入
接下来是如何进行数据的输入,和对c[i]数组的维护
因为树状数组中的c[i]数组保存的是前面数据的和,故输入一个数据,只会对后面的数组有影响。比如在上面的图一中,输入数据a[4],只会对c[4]和c[8]有影响。可以发现如a[11],a[100],a[110],先更新本身c[11],c[100],c[110],然后在更新上一层,分别为c[100],c[1000],c[1000].依次向上更新,每次只需在x基础上加lowbit(x)

实现代码:

void update(int x,int val)//x节点的值为val
{
	while(x<MAX)//MAX为c数组上限
	{
		c[i]+=val;
		x+=lowbit(x);
	}
}

求和
求出区间1~x的和
sum=0;
例如求x=1000:
sum+=c[1000]
再比如求x=110,可以根据图一看出等于c[110]+c[100],即

  1. sum+=c[x];
  2. sum+=c[x-lowbit(x)]

实现代码

int add(int x)
{
	int sum=0;
	while(x>0)
	{
		sum+=c[x];
		x-=lowbit(x);
	}
	return sum;
}

若求[a,b]区间的和则为add(b)-add(a-1)


二维树状数组

二维树状数组,简单讲就是在update,add函数中while里再嵌套一个while,具体看代码:

int Row=100;
int Col=100;

int lowbit(int x)
{
	return x&(-x);
}

void update(int i,int j,int val)
{
	while(i<=Row)
	{
		int tj=j;
		while(tj<=Col)
		{
			c[i][tj]+=val;
			tj+=lowbit(tj);
		}
		i+=lowbit(i);
		
	}
}


int add(int i,int j)//计算矩形(1,1)~(i,j)的和
{
	int sum=0;
	while(i>0)
	{
		int tj=j;
		while(tj>0)
		{
			sum+=c[i][tj];
			tj-=lowbit(tj);
		}
		i-=lowbit(i);
		
	}
	return sum;
}

例题:poj1656
代码:

#include <cstdio>
#include <cstring>
#include <iostream>

using namespace std;
int c[101][101];
int se[101][101];
string str;
int x,y,L;
int t;
int Row=100;
int Col=100;

int lowbit(int x)
{
	return x&(-x);
}

void update(int i,int j,int val)
{
	while(i<=Row)
	{
		int tj=j;
		while(tj<=Col)
		{
			c[i][tj]+=val;
			tj+=lowbit(tj);
		}
		i+=lowbit(i);
		
	}
}


int add(int i,int j)
{
	int sum=0;
	while(i>0)
	{
		int tj=j;
		while(tj>0)
		{
			sum+=c[i][tj];
			tj-=lowbit(tj);
		}
		i-=lowbit(i);
		
	}
	return sum;
}


int main()
{
	cin>>t;
	while(t--)
	{
		cin>>str;
		cin>>x>>y>>L;
		if(str=="BLACK")
		{
			for(int i=x;i<x+L;i++)
				for(int j=y;j<y+L;j++)
				{
					if(se[i][j]==1) continue;
					else {
						se[i][j]=1;
						update(i,j,1);
					}
				}		
		}
		else if(str=="WHITE")
		{
			for(int i=x;i<x+L;i++)
				for(int j=y;j<y+L;j++)
				{
					if(se[i][j]==0) continue;
					else {
						se[i][j]=0;
						update(i,j,-1);
					}
				}	
			
		}
		else if(str=="TEST")
		{
			int sum=add(x+L-1,y+L-1)-add(x-1,y+L-1)-add(x+L-1,y-1)+add(x-1,y-1);
			cout<<sum<<endl;
		}
	}
	
	/* while(t--)
	{
		cin>>str;
		if(str=="BLACK")
		{
			cin>>x>>y>>L;
			for(int i=x;i<=x+L-1;i++)
				for(int j=y;j<=y+L-1;j++)
					f[i][j]=1;
		}
		else if(str=="WHITE")
		{
			cin>>x>>y>>L;
			for(int i=x;i<=x+L-1;i++)
				for(int j=y;j<=y+L-1;j++)
					f[i][j]=0;
			
		}
		else if(str=="TEST")
		{
			cin>>x>>y>>L;
			int cnt=0;
			for(int i=x;i<=x+L-1;i++)
				for(int j=y;j<=y+L-1;j++)
					if(f[i][j]==1) cnt++;
			cout<<cnt<<endl;
		}

	} */
	
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值