hdu 1166 敌兵布阵(线段树)

xiaoxiao2021-02-28  92

刚学习了线段树,找个模板题练练手;

先说什么是线段树:(以下定义来自百度百科)

    线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。 使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩。

线段树的操作有以下几种:

1.建树;

2.查询;

3.点更新;

4.区间更新;

这个题呢很模板,一个建树,一个查询,一个点更新;三个函数完事;

没有涉及到区间更新(这个才是线段树的大头);

先说一下建树:

找区间1-10中的最小值;

1-10为1,2,3,4,5,6,7,8,9,10;

void build(int node, int left, int right, int &vel){//传参为树根, 左边界, 右边界, 所需叶节点的值; //该树与题目无关,vel值从1到10; if(left==right){ //if左右边界相等则到达树的叶子,赋值,每次赋完值,vel自增; segtree[node]=vel; vel++; return; } build(node*2, left, (left+right)/2, vel); // 先建左子树; build(node*2+1, (left+right)/2+1, right, vel);//后建右子树; segtree[node]=min(segtree[node*2],segtree[node*2+1]);//父节点存最小的数值; }

然后是点更新:

void updata(int node, int left, int right, int ind, int add){//传参为树根,左右边界,改变的点,增加的值; if(left==right){ //if左右边界相等,找到该点,该结点值改变; segtree[node]+=add; return; } int mid=(left+right)>>1;//相当于(left+right)/2; if(ind<=mid) //改变的点在左边,找左子树; updata(node*2, left, mid, ind, add); else //在右边,找右子树; updata(node*2+1, mid+1, right, ind, add); segtree[node]=min(segtree[node*2],segtree[node*2+1]);//维护父结点一直是最小值; }查询:

int query(int node, int beginn, int endd, int left, int right)//传参为树根, 已知的区间边界,所需查询的区间边界; { int p1, p2; if (left > endd || right < beginn) //查询的区间不在已知区间,即找不到查询区间,返回无穷; return INF; if (beginn >= left && endd <= right) //查询区间比已知区间大,直接返回segtree[node];根结点; return segtree[node]; return min(query(2 * node, beginn, (beginn + endd) / 2, left, right) query(2 * node + 1, (beginn + endd) / 2 + 1, endd, left, right));//分别到左右找符合查询区间的min; }

下面看一下例题  :

                                                                                                     敌兵布阵 

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的责骂的. Input     第一行一个整数T,表示有T组数据。     每组数据第一行一个正整数N(N<=50000),表示敌人有N个工兵营地,接下来有N个正整数,第i个正整数ai代表第i个工兵营地里开始时有ai个人(1<=ai<=50)。     接下来每行有一条命令,命令有4种形式:     (1) Add i j,i和j为正整数,表示第i个营地增加j个人(j不超过30)     (2)Sub i j ,i和j为正整数,表示第i个营地减少j个人(j不超过30);     (3)Query i j ,i和j为正整数,i<=j,表示询问第i到第j个营地的总人数;     (4)End 表示结束,这条命令在每组数据最后出现;     每组数据最多有40000条命令 Output     对第i组数据,首先输出“Case i:”和回车,     对于每个Query询问,输出一个整数并回车,表示询问的段中的总人数,这个数保持在int以内。 Sample Input     1     10     1 2 3 4 5 6 7 8 9 10     Query 1 3     Add 3 6     Query 2 7     Sub 10 2     Add 6 3     Query 3 10     End  Sample Output     Case 1:     6     33

    59

下面就上代码:

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <iostream> #include <algorithm> using namespace std; #define MAX 50005 int segtree[MAX*4]; int arr[MAX]; void build(int node, int left, int right){ //建树;传参为树根, 左边界, 右边界; if(right==left){ segtree[node]=arr[left]; return; } int mid=(left+right)/2; build(node*2, left, mid); build(node*2+1, mid+1, right); segtree[node]=segtree[node*2]+segtree[node*2+1]; } void add(int node, int left, int right, int ind, int x){//点更新;传参为树根,左边界, 右边界, 改变的点, 增加的值; if(left==right){ segtree[node]+=x; return; } int mid=(left+right)/2; if(ind<=mid) add(node*2, left, mid, ind, x); else add(node*2+1, mid+1, right, ind, x); segtree[node]=segtree[node*2]+segtree[node*2+1]; } int query(int node, int left, int right, int l, int r){//查询;传参为树根, 已知左边界右边界, 查询的左边界右边界; if(l<=left && r>=right){ return segtree[node]; } int mid=(left+right)/2; if(r<=mid) return query(node*2, left, mid, l, r); else if(l>mid) return query(node*2+1, mid+1, right, l, r); else return query(node*2, left, mid, l, mid) + query(node*2+1, mid+1, right, mid+1, r); } int main(){ int T; scanf("%d",&T); int cnt=0; while(T--){ printf("Case %d:\n",++cnt); int N; scanf("%d",&N); int i; for(i=1; i<=N; i++) scanf("%d",&arr[i]); build(1, 1, N); char s[20]; while(scanf("%s",s)){ if(s[0]=='E') break; int a, b; scanf("%d%d",&a,&b); if(s[0]=='A') add(1, 1, N, a, b); else if(s[0]=='S') add(1, 1, N, a, 0-b); else{ int sum; sum=query(1, 1, N, a, b); printf("%d\n",sum); } } } return 0; }

转载请注明原文地址: https://www.6miu.com/read-96209.html

最新回复(0)