A - 敌兵布阵 HDU - 1166(segment_tree的单点修改+区间求和,基础!!)

在这里插入图片描述
在这里插入图片描述
这道题题意很简单就是如果字符为Q那么就询问[i,j]区间的和,如果为U,那么就更新i点值为j;
线段树基础题目;
那什么是线段树?
我自己是这样理解的:
线段是本质上就是一个区间的映射为一个节点+二叉树的后序遍历;著名的RMQ问题就是很基础的线段树操作;
几何理解:
在这里插入图片描述
可以发现线段树就是把区间分块,然后存下对应区间的问题规模(比如这个区间Max或者Min或者Sum或者平均值。。等等);
可以很明显发现这个二叉树的子叶节点就是L==R(这里要很明白,因为对后面的递归很有帮助!!)
所以这个问题就很容易了,我只需要把问题规模存下来,然后递归一遍就OK了(如果你对二叉树的三种遍历方式很熟悉,理解这个线段树的递归就很容易了);
线段树的本质就是这个了;
但是如何Build和操作呢?
建树时利用二叉树的后序遍历+Tree来循序存储线段树的节点对应的值;
意思就是:
在这里插入图片描述
可以很明显发现子节点和父节点的index的关系;所以利用这个关系我就可以Build了;
也就是(这里就建立好了线段树的逻辑存储结构了):

void Build(int root,int L,int R){//建树 
	  if(L==R){//当左端点==有端点时说明到了叶结点
	  	   scanf("%d",&Tree[root]);//根据节点编号输入;
	  	   //cout<<":"<<Tree[L]<<" ";
			 return;
	  }else{
	      int mid=(L+R)/2;//二叉树的后续遍历用来建立树,至于为什么看看书就可以理解了;
		  Build(2*root,L,mid);
		  Build(2*root+1,mid+1,R);
		  Tree[root]=Tree[root*2]+Tree[root*2+1];	
		  //cout<<Tree[root]<<" ";
	  }
}

然后线段树的单点更新,应该如何操作呢?肯定是递归下去,因为单点更新肯定会给index所以我按照index和区间[L,R]的包含关系进行递归知道L==R时说明找到了该点,然后就可以修改;但是为什么呢?原因很简单我可以发现线段树有个这样的特点:
就是它的子叶节点对应的区间(可以很明显发现哦,根据上面的图)其实就是元素在原数组的下标(因为这里我简化内存没有写成Tree[root]=a[L],而是直接输入的);这点很重要!!所以根据这个我就可以递归找到端点然后改值,然后递归上去把父节点的值也跟着改动;
所以思路就是这样滴(如果不能理解递归的建议把数据结构的树的知识理解理解就好了,嘻嘻);
本题的AC代码:

#include<iostream>
#include<string>
#include<cstdio>
#include<set>
#include<map> 
using namespace std;
const int maxn=5e4+10;
int Tree[maxn<<2],T,n;
int ans[maxn];
void Build(int root,int L,int R){//建树 
	  if(L==R){//递归建二叉树
	  	   scanf("%d",&Tree[root]);
	  	   //cout<<":"<<Tree[L]<<" ";
			 return;
	  }else{
	      int mid=(L+R)/2;
		  Build(2*root,L,mid);
		  Build(2*root+1,mid+1,R);
		  Tree[root]=Tree[root*2]+Tree[root*2+1];	
		  //cout<<Tree[root]<<" ";
	  }
}
int Query(int root,int L,int R,int l,int r){//区间和 //其实这个代码写法并不好!只不过比较好理解些
	  if(l<=L&&R<=r)return Tree[root];//如果对于的区间重合时,那么就表示这个区间刚好对应值
	  	  int mid=(L+R)/2;//这里用笔算一下就知道了;
	  	  if(r<=mid) return  Query(2*root,L,mid,l,r);//如果整个区间都在系统区间的mid的左边
	  	  if(l>mid) return Query(2*root+1,mid+1,R,l,r);//如果整个区间都在系统区间的mid的右边
	  	 return Query(2*root,L,mid,l,mid)+Query(2*root+1,mid+1,R,mid+1,r);//分两边走
}
void updatapoint(int root,int L,int R,int x,int val){//单点更新 
	   if(L==R){//到了叶结点
	   	  Tree[root]+=val;return ;
	   }
	   int mid=(L+R)/2;
	   if(x<=mid)updatapoint(2*root,L,mid,x,val);//这里可以这样理解:如果x在mid的左边,那么我就去左边区间找
	   else updatapoint((root<<1)|1,mid+1,R,x,val);//如果x在mid的右边,那么我就去右边区间找
	   Tree[root]=Tree[root<<1]+Tree[(root<<1)|1];//然后求父节点对应的子节点的和,这里的的2*root和2*root+1我用位移运算符+逻辑运算符表示的
}
int main(){
	scanf("%d",&T);
 char s[10]; //注意这里题上说一个字符,但是我也不知道为什么用char s就wa了,只好改为数组了;估计应该是读文件的时候的问题
	int a,b;
for(int i=1;i<=T;i++){
	int g=0;
		scanf("%d",&n);
		Build(1,1,n);//建树
	while(scanf("%s",s)){
		if(s[0]=='E')break;
		scanf("%d %d",&a,&b);
		if(s[0]=='A'){
			updatapoint(1,1,n,a,b);
		}else if(s[0]=='S'){
			updatapoint(1,1,n,a,-b);
		}else{
			ans[g++]=Query(1,1,n,a,b);
		}
	  }
	  printf("Case %d:\n",i);
	  for(int j=0;j<g;j++)printf("%d\n",ans[j]); 
	}
		return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了。A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务就是要监视这些工兵营地的活动情况。由于采取了某种先进的监测手段,所以每个工兵营地的人数C国都掌握的一清二楚,每个工兵营地的人数都有可能发生变动,可能增加或减少若干人手,但这些都逃不过C国的监视。 中央情报局要研究敌人究竟演习什么战术,所以Tidy要随时向Derek汇报某一段连续的工兵营地一共有多少人,例如Derek问:“Tidy,马上汇报第3个营地到第10个营地共有多少人!”Tidy就要马上开始计算这一段的总人数并汇报。但敌兵营地的人数经常变动,而Derek每次询问的段都不一样,所以Tidy不得不每次都一个一个营地的去数,很快就精疲力尽了,Derek对Tidy的计算速度越来越不满:"你个死肥仔,算得这么慢,我炒你鱿鱼!”Tidy想:“你自己来算算看,这可真是一项累人的工作!我恨不得你炒我鱿鱼呢!”无奈之下,Tidy只好打电话向计算机专家Windbreaker求救,Windbreaker说:“死肥仔,叫你平时做多点acm题和看多点算法书,现在尝到苦果了吧!”Tidy说:"我知错了。。。"但Windbreaker已经挂掉电话了。Tidy很苦恼,这么算他真的会崩溃的,聪明的读者,你能写个程序帮他完成这项工作吗?不过如果你的程序效率不够高的话,Tidy还是会受到Derek的责骂的.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值