线段树基础知识(续)

32 篇文章 1 订阅
7 篇文章 0 订阅

经过上次线段树的基本讲解,这次我们就专门来看几道例题。

---------------------------------------------------------------------------------------------------------

问题 D(1911): 【高级数据结构】线段的条数

题目描述

无限长的X轴上从下向上依次叠放一定长度某种线段。问在某个单位区间上一共叠放了多少条线段?

输入

第1行:1个整数N。1<=N<=100000,表示线段的条数

接下来N行,每行2个整数L,R,-100000 <=L < R<= 100000,表示一线段的左、右端点(左闭右开区间);
最后1行:1个整数P,表示单位区间的起点。-100000 <=P<100000

输出

第1行:1个整数M,表示[P, P+1) ]区间叠放了多少条线段

样例输入

5
-1 5
3 10
-7 7
3 4
-9 2
-4

样例输出

2


分析

首先这道题毋庸置疑使用线段树来做,和往前一样来做。
build,insert,find都是基本的线段树函数,我们可以得出思路: 为线段树每个节点增加一个cnt标记。表示所对应区间上重叠的线段数。
我们又思考一下线段树的构造方法:当某线段能够完整覆盖某个结点所对应的区间时,则不再二分。因此要统计某个单位区间上重叠的线段总数,必须把从叶结点到根结点路径上所有结点的count标记累加。
所以只需更新插入算法和统计算法即可。
void insert(int k,int l,int r){//插入算法
    if(r<tree[k].l||tree[k].r<l) return;
    if(l<=tree[k].l&&tree[k].r<=r){
        tree[k].cnt++;
        return;
    }
    insert(k*2,l,r);
    insert(k*2+1,l,r);
}
/**********************************************/
void find(int k,int l,int r){//统计算法
    if(r<tree[k].l||tree[k].r<l) return;
    ans+=tree[k].cnt;
    find(k*2,l,r);
    find(k*2+1,l,r);
}


以下就是我的源代码:
#include<cstdio>
#include<iostream>
using namespace std;
const int maxn=200001;
struct node{
    int l,r;
    int cnt;
}tree[maxn*4];
int N,P;
int ans;
void build(int k,int l,int r){
    tree[k].l=l;
    tree[k].r=r;
    if (l==r) return;
    int mid=(l+r)>>1;
    build(k*2,l,mid);
    build(k*2+1,mid+1,r);
}
void insert(int k,int l,int r){
    if(r<tree[k].l||tree[k].r<l) return;
    if(l<=tree[k].l&&tree[k].r<=r){
        tree[k].cnt++;
        return;
    }
    insert(k*2,l,r);
    insert(k*2+1,l,r);
}
void find(int k,int l,int r){
    if(r<tree[k].l||tree[k].r<l) return;
    ans+=tree[k].cnt;
    find(k*2,l,r);
    find(k*2+1,l,r);
}
int main()
{
    int add=100000;
    build(1,0,200000);
    scanf("%d",&N);
    int L,R;
    for(int i=1;i<=N;i++){
        scanf("%d%d",&L,&R);
        L+=add;R+=add;
        insert(1,L,R-1);
    }
    scanf("%d",&P);
    P+=add;
    find(1,P,P);
    printf("%d",ans);
}

---------------------------------------------------------------------------------------------------------

问题 E(1910): 【高级数据结构】子区间的和

题目描述

一行N个方格,开始每个格子里的数都是0。现在动态地提出一些问题和修改:提问的形式是求某一个特定的子区间[A,B]中所有元素的和;修改的规则是指定一个位置X,将该元素都加上一个特定的值A(A可能为负)。

现在要求你能对每个提问作出正确的回答。

输入

第1行:2个整数N(1<=N<=1000000),Q(1<=Q<=60000),N表示格子的数量,编号从1..N。Q表示询问和修改的总数。

接下来Q行,每行的格式为:3个整数P ,A,B。第1个数P(0<=P<=1),表示操作类型。当P=0时,表示将编号为A的格子加上数B。当P=1时,表示询问区间[A,B]之间的和。

输出

若干行,依次输出每个询问操作的结果,每个询问占一行。

样例输入

1 4
0 1 100
1 1 1
0 1 -100
1 1 1

样例输出

100
0

分析

用线段树解

为线段树每个节点增加一个sum标记,表示所对应区间内元素之和。
每次修改一个格子,需要修改从叶结点到根结点路径上所有结点的值。
为了定位到元素x,可以递归地从根查找到叶结点,然后在返回段修改值。
也可以用下面示例的方法做修改。
区间求和则是线段树的基本应用。
void modify(int p, int delta)//插入算法
//p表示要修改的元素x在线段树中的位置
{
	do {
		tree[p].sum += delta;
		p /= 2;
	}while(p > 0);
}
/*
要预处理出存每个元素在线段树的叶节点(单位区间)的位置。
对于查询次数非常多的题目,这样预处理是有价值的。
*/
/**********************************************/
void count(int i,int l,int r)//统计算法
{
    if (r<tree[i].l || tree[i].r<l) return;
    if (l<=tree[i].l && tree[i].r<=r)
    {
        ans+=tree[i].sum;
        return;
    }
    count(i*2,l,r);
    count(i*2+1,l,r);
}


以下就是我的源代码:
#include<cstdio>
#include<cstring>
#define lowbit(x) ((x)&(-x))
int tree[1000005],N,T,Q;
void update(int pos,int val){
    while(pos<=N){
        tree[pos]+=val;
        pos+=lowbit(pos);  
    } 
}
int getsum(int x){
    int sum=0;
    while(x>0){
        sum+=tree[x];
        x-=lowbit(x);
    } 
    return sum;
}
int main()
{
    int x;
	scanf("%d%d",&N,&Q);
    while(Q--){
		int p,a,b;
		scanf("%d%d%d",&p,&a,&b);
        if(!p) update(a,b);
		if(p) printf("%d\n",getsum(b)-getsum(a-1));
    }
}
---------------------------------------------------------------------------------------------------------

问题 F(1909): 【高级数据结构】二维区间的和

题目描述

假定在 Tampere 地区的第四代移动电话基站这样运行:整个地区分成方块,方块形成 S×S 矩阵,行和列编号从 0 S-1. 每个方块包含一个基站。在每个方块中的活动移动电话数可以改变,因为电话能从一个方块移动到另一个方块,或者电话能开机或关机。每过一段时间,每个基站都向主站报告自身活动电话的改变情况,以及自己在矩阵中的行列位置。
写一个程序,能够接收这些基站的报告,并回答关于指定矩形区域内的活动移动电话数量的询问。

输入

由若干行组成,每行由一个指令字 ( 整数 ) 和一些整数参数组成。指令字含义为:
指令 参数 意义
0 S  初始化 S*S  的区域为全 0 ,该指令仅在开头出现一次。(1<=S<=1500)
1 X Y A  在方块 (X,Y) 处加上 A 个活动移动电话, A 可为正可为负。
L  B R T  询问各个方块中活动移动电话的数量之和, L ≤ X ≤ R, B ≤ Y ≤ T
结束程序,该指令仅在结束时出现一次。
保证数据在给定范围内,不必检验。如果 A 是负数 可以保证不会使某个方块的值减小为负数。下标从 0 开始,也就是说,对于  4×4  的区域  0 ≤ X ≤ 3   0 ≤ Y ≤ 3

输出

对每一个指令字为2的输入,在一行上回答一个询问

样例输入

 (如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)

0 4
1 1 2 3
2 0 0 2 2
1 1 1 2
1 1 2 -1
2 1 1 2 3
3

样例输出

3
4

提示

输入         输出           解释
0 4                              初始化4×4的区域. 
1 1 2 3                       用+3 更新 方块(1,2)  
2 0 0 2 2                    询问区域 0 ≤ X ≤ 2,  0 ≤ Y ≤  2.  
                     3            回答询问 
1 1 1 2                       用+2 更新 方块 (1, 1) 
1 1 2 -1                      用-1 更表 方块(1, 2)  
2 1 1 2 3                     询问区域  ≤ X ≤ 2,  1 ≤ Y ≤  3.  
                      4            回答询问 
3                                  结束 


#include<cstdio>
#include<cstring>
#define lowbit(x) (x&-x)
#define MAXN 1029 
int bittree[MAXN][MAXN]; 
int op,s,x1,y1,x2,y2,a; 
void update(int posx,int posy,int val){ 
    for(int i=posx;i<=s;i=i+lowbit(i))
        for(int j=posy;j<=s;j=j+lowbit(j))
            bittree[i][j]+=val;
} 
int getsum(int posx,int posy){
    int sum=0; 
    for(int i=posx;i>0;i=i-lowbit(i)) 
        for(int j=posy;j>0;j=j-lowbit(j)) 
            sum+=bittree[i][j]; 
    return sum; 
}
int main()
{
	int p;
	while(~scanf("%d",&p)){
		if(p==3) return 0;
		else if(p==1){
			int x,y,a;
			scanf("%d%d%d",&x,&y,&a);
			update(x+1,y+1,a);
		}
		else if(p==2){
			scanf("%d%d%d%d",&x1,&y1,&x2,&y2); 
            x1++,y1++,x2++,y2++; 
			printf("%d\n",getsum(x2,y2)-getsum(x1-1,y2)-getsum(x2,y1-1)+getsum(x1-1,y1-1));
		}
		else if(p==0) scanf("%d",&s);
	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值