Hud 敌兵布阵 --线段树的插点问线

     每个树存放的事左右端点的的总和,然后再递归往下分成更小的区间,根据着2*t是下一层节点的节点端点是(left,left+right)/2);2*t+1是右节点的区间是(left+right)/2+1,right);

而更新的时候就是把点当成是(x,x)这样的区间然后去寻找它所在区间的叶子节点,然后回溯把所有祖先的区间都更新了,然后即使查找的了,当你查找的区间是(l,r)时,你先是从最大的区间开始去查找也就是从节点等于一开始查找看是否与所求的区间相等,不等时有三种可能就是在左右节点或者是左右节点的区间各有一半,然后就分开讨论。

 

#include<stdio.h>
#include<string.h>
#define mid(x,y) (x+y)/2
int v,b;
int a[500005],ans,cnt;
struct app
{
	int left,right;
	int sum;
}tree[500000];
void build_tree(int left,int right,int t)   //建树,l,r是区间的左右端点数,t是每次从从1开始然后繁衍儿子
{
	int x;
	tree[t].left=left;
	tree[t].right=right;
	tree[t].sum=0;
	if(left==right)
	{                   //当它已经是叶子了不能在繁衍了就开始回溯让它的所有祖先都加上儿子(敌营)的人数
	       while(t!=0)    
		   {
			   tree[t].sum+=a[left];
			   t/=2;        //下一个祖先
		   }
		   return ;
	}
  x=mid(left,right);
  build_tree(left,x,2*t);    //对每个区间不断的递归让它们能不断分离下去,注意哦,这里是不断找出每个区间的左区间,直到找完了再开始从头找右节点  
  build_tree(x+1,right,2*t+1);
}

void updata_tree(int left,int right,int t)  //更新线段树,先找到所更新点的叶子,然后再把此点和所有的祖先区间都更新
{
	if(tree[t].left==left&&tree[t].right==right)
	{
	    while(t!=0)
		{
			tree[t].sum+=cnt;
			t/=2;
		}
		return ;
	}
	int x=mid(tree[t].left,tree[t].right);
	if(x>=right)    //  分到的点可以锁定在右边的区间所以,关键在2*t着才是代表在右边
     updata_tree(left,right,2*t);
	else if(x+1<=left)
		updata_tree(left,right,2*t+1);
	else
	{
		updata_tree(left,x,2*t);
		updata_tree(x+1,right,2*t+1);
	}
}

void query_tree(int left,int right,int t)    //查找区间如果是在区间里面的就直接把和加上,如果是是在左右区间的中间那就分开加直到你加到你查找的区间被包含在现有的区间内,就结束了然后把你所经历的区间的值加起来就是结果了
{
   if(tree[t].left==left&&tree[t].right==right)
   {
	   ans+=tree[t].sum;
	   return ;
   }
   int x=mid(tree[t].left,tree[t].right);
   if(x>=right)
   {
	   query_tree(left,right,2*t);
   }
   else if(x+1<=left)
	   query_tree(left,right,2*t+1);
   else
   {
	   query_tree(left,x,2*t);
	   query_tree(x+1,right,2*t+1);
   }
}



int main()
{
	int t,n,i,j;
	scanf("%d",&t);
	for(i=1;i<=t;i++)
	{
		printf("Case %d:\n",i);
		scanf("%d",&n);
		memset(a,0,sizeof(a));
		for(j=1;j<=n;j++)
			scanf("%d",&a[j]);
		build_tree(1,n,1);
		char sh[10];
		while(1)
		{
			scanf("%s",sh);
			if(sh[0]=='E')
				break;
			if(sh[0]=='A')
			{
				scanf("%d%d",&v,&cnt);
				updata_tree(v,v,1);
			}
			if(sh[0]=='Q')
			{
				scanf("%d%d",&v,&b);
				ans=0;
				query_tree(v,b,1);
				printf("%d\n",ans);
				
			}
			if(sh[0]=='S')
			{
				scanf("%d%d",&v,&cnt);
				cnt=-cnt;
				updata_tree(v,v,1);
			}
		}
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值