刚学了线段树,以士兵杀敌(一)为例,简单介绍一下。
士兵杀敌(一):http://115.159.40.116/problem_show.php?pid=4720
简单介绍线段树
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。
【准备工作】
二叉树的每一个节点和子叶,都是用结构体来记录信息的;
#include<stdio.h>
#include<string.h>
#define N 1000010
struct node
{
int v;
int l,r;//l为区间的左边界,r为区间的右边界;
}edge[N*4+10];//新的结构体
int dis[N];//原数组,题目给的士兵杀人数;
【创建树】
创建如下的一棵树
//buildtree(1,1,n);【1,n】之间的数
//num,从1开始编号;
//l,从原数组的第一位开始
//r,从原数组的最后一位开始
void buildtree(int num,int l,int r)
{
edge[num].l=l;
edge[num].r=r;
if(l==r)
{
edge[num].v=dis[l];
return ;
}
int mid=(r+l)/2;
buildtree(num<<1,l,mid);//对左子树进行编号
buildtree(num<<1|1,mid+1,r);//对右子树进行编号
edge[num].v=edge[num<<1].v+edge[num<<1|1].v;//递归求和
}
【求和】
int query(int num,int l,int r)
{
if(edge[num].l>=l&&edge[num].r<=r)//这部分在【l,r】范围内,需要计算;
return edge[num].v;
if(edge[num].l>r||edge[num].r<l)//超出【l,r】范围的部分不做统计;
return 0;
return query(num<<1,l,r)+query(num<<1|1,l,r);//返回左子树和右子树的和(即为【l,r】的和);
}
完整代码如下:
#include<stdio.h>
#include<string.h>
#define N 1000010
struct node
{
int v;
int l,r;
}edge[N*4+10];
int dis[N];
void buildtree(int num,int l,int r)
{
edge[num].l=l;
edge[num].r=r;
if(l==r)
{
edge[num].v=dis[l];
return ;
}
int mid=(r+l)/2;
buildtree(num<<1,l,mid);
buildtree(num<<1|1,mid+1,r);
edge[num].v=edge[num<<1].v+edge[num<<1|1].v;
}
int query(int num,int l,int r)
{
if(edge[num].l>=l&&edge[num].r<=r)//这部分在【l,r】范围内,需要计算;
return edge[num].v;
if(edge[num].l>r||edge[num].r<l)//超出【l,r】范围的部分不做统计;
return 0;
return query(num<<1,l,r)+query(num<<1|1,l,r);//返回左子树和右子树的和(即为【l,r】的和);
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
memset(dis,0,sizeof(dis));
for(int i=1;i<=n;i++)
{
scanf("%d",&dis[i]);
}
buildtree(1,1,n);
int s,e;
while(m--)
{
scanf("%d%d",&s,&e);
if(s>e)
{
int r=s;
s=e;
e=r;
}
printf("%d\n",query(1,s,e));
}
return 0;
}