线段树 hdu 1166

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1166

线段树第一题~

       分割线///

线段树:一种二叉搜索树,类似于区间树,各个字节点为线段或者单元区间,能基本保证每个操作的复杂度为O(logn).对于一个非叶节点区间[a,b],其左儿子对应的区间为[a,(a+b)/2],右儿子对应的区间为[(a+b)/2+1,b],因此该树也是平衡二叉树,叶节点个数为b-a+1.

线段树的基本操作:

1>线段树的构造

由于树本身是一种递归结构,所以构造树也是一种递归的思想,下面可以看成伪代码

function  构造区间为[a,b]的线段树

 if 区间内元素为1,即b-a=0

   区间的元素值就是这个元素,最小值也即这个元素 (eg:[1,10]中,[1,1],[2,2],[3,3]区间值最小值是1,2,3)

 else

   把区间一分为2,构造[a,(a+b)/2]和[(a+b)/2+1,b]区间的数

 更新结点之间信息

   区间的信息(sum,min)=(sum,min)(左儿子+右儿子).

2>区间查询

 function 查询区间[a,b]的信息

 if 区间的左值==a&&区间的右值==b

   取得相应信息,return;

 int mid=(lson+rson)/2;  //mid为分隔点

 if(b<=mid)

  查询左儿子部分的信息

 if(a>mid)

 查询右儿子部分信息

 else

 {

  查询[a,mid];

  查询[mid+1,b];

 }

}

此题用的静态数组做,也可以用链表来做~

 

AC code:

#include<stdio.h>
#include<iostream>
#include<cstring>
using namespace std;
struct T
{
int lson,rson,n;          //结构体定义左右结点区间
}Tree[150001];
int sum;
void InitTree(int l,int r,int k)
{
if(l==r)
{
Tree[k].lson=l;
Tree[k].rson=r;
Tree[k].n=0;
return ;
}
else
{
Tree[k].lson=l;
Tree[k].rson=r;
Tree[k].n=0;
int mid=(l+r)/2;
InitTree(l,mid,2*k);   //构造左半区间
InitTree(mid+1,r,2*k+1); //构造右半区间
    }
}
void Insert(int num,int add,int k)
{
if(Tree[k].lson==num&&Tree[k].rson==num) //找到单元区间,增加add
{
Tree[k].n+=add;
return;
}
else
{
int mid=(Tree[k].lson+Tree[k].rson)/2;
if(num>mid)          //大于中值,根据树的结构,在右边查找
Insert(num,add,2*k+1);
else
Insert(num,add,2*k);    //小于中值,在左树查找
}
Tree[k].n=Tree[2*k].n+Tree[2*k+1].n;        //更新结点值
}


void Query(int s,int d,int k)
{
if(Tree[k].lson==s&&Tree[k].rson==d)            //找到区间,把值存到sum中
{ sum=sum+Tree[k].n; return;}
else
{
int mid=(Tree[k].lson+Tree[k].rson)/2;
if(d<=mid)                   //尾部小于中值,则查找区间为树的左边
   Query(s,d,2*k);
else if(s>mid)            //起点大于中值,则查找区间为树的右边
Query(s,d,2*k+1);
else              //划分区间,合并值
{
Query(s,mid,2*k);        //s~mid
Query(mid+1,d,2*k+1);        //mid+1~d
   }
}
}
int main()
{
int i,j,test,num,t,flag,dex,dey,Count=0;
char str[10];
scanf("%d",&test);
while(test--)
{
Count++;
flag=1;
scanf("%d",&num);
InitTree(1,num,1);
for(i=1;i<=num;++i)
{
scanf("%d",&t);
Insert(i,t,1);
}
cin>>str;
if(str[0]=='E') { flag=0; continue;}
cout<<"Case "<<Count<<":"<<endl;
while(1)
{
sum=0;
if(str[0]=='E') { flag=0; break;}
scanf("%d%d",&dex,&dey);
if(str[0]=='Q')
{
Query(dex,dey,1);
printf("%d\n",sum);
   }
if(str[0]=='A')
Insert(dex,dey,1);
if(str[0]=='S')
Insert(dex,-dey,1);
cin>>str;
}
if(!flag) continue;
}
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值