HDU——1166 敌兵布阵(线段树入门模板题——区间求和)

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

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

题意:给你一个序列,会进行修改和查询操作,请实现这些功能并打印查询的结果。

解题思路:这是一道线段树区间求和模板题,理解题意按模板做即可,淡然,你肯定要对线段树比较熟悉。若对线段树入门不太了解的话指路一篇博客:https://blog.csdn.net/hzf0701/article/details/107859659

AC代码:

/*
*邮箱:2825841950@qq.com
*blog:https://blog.csdn.net/hzf0701
*注:代码如有问题请私信我或在评论区留言,谢谢支持。
*/
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<string>
#include<stack>
#include<queue>
#include<cstring>
#include<map>
#include<iterator>
#include<list>
#include<set>
#include<functional>
#include<memory.h>//低版本G++编译器不支持,若使用这种G++编译器此段应注释掉
#include<iomanip>
#include<vector>
#include<cstring>
#define scd(n) scanf("%d",&n)
#define scf(n) scanf("%f",&n)
#define scc(n) scanf("%c",&n)
#define scs(n) scanf("%s",n)
#define prd(n) printf("%d",n)
#define prf(n) printf("%f",n)
#define prc(n) printf("%c",n)
#define prs(n) printf("%s",n)
#define rep(i,a,n) for (int i=a;i<=n;i++)//i为循环变量,a为初始值,n为界限值,递增
#define per(i,a,n) for (int i=a;i>=n;i--)//i为循环变量, a为初始值,n为界限值,递减。
#define pb push_back
#define fi first
#define se second
#define mp make_pair
using namespace std;
const int inf = 0x3f3f3f3f;//无穷大
const int maxn = 5e5+1;//最大值。
typedef long long ll;
typedef long double ld;
typedef pair<ll, ll>  pll;
typedef pair<int, int> pii;
//*******************************分割线,以上为代码自定义代码模板***************************************//

struct Node{
    int left;  //左端点
    int right; //右端点
    int value;//代表区间[left,right]的信息,可以是区间和,也可以是区间最值。
}node[maxn<<2];//这里我们要开4倍大小,防止数据溢出
int father[maxn];//存储原来数据在线段树中的下标,易于从下向上更新区间数据。例如father[i]表示原来的第i个数据在线段树中的下标,这些在线段树中都是叶子结点。
void BuildTree(int i,int l,int r){
    node[i].left=l;node[i].right=r;//存储各自结点的区间
    node[i].value=0;                     //初始化为0.
    if(l==r){                    //说明已经到了叶子结点。
        father[l]=i;//存储下标。
        return;
    }
    BuildTree(i<<1,l,(l+r)/2); //递归初始化左子树
    BuildTree((i<<1)+1,(l+r)/2+1,r);//递归初始化右子树。
}
//更新线段树
void UpdateTree(int ri){
    if(ri==1){
        return;
    }
    int fi=ri>>1;//获得父结点下标。
    node[fi].value=node[fi<<1].value+node[fi<<1|1].value;//两段区间总和。
    UpdateTree(fi);
}
//区间查询,调用函数时为QueryTree(1,l,r),即从根节点自上往下查询。
int QueryTree(int i,int l,int r){
    int sum=0;
    if(l==node[i].left&&r==node[i].right){
        //如果刚好就是这个区间,我们直接返回。
        sum+=node[i].value;
        return sum;
    }
    i=i<<1;
    if(l<=node[i].right){
        //说明部分包含左子树
        if(r<=node[i].right){
            //说明全包含在左子树。
            sum+=QueryTree(i,l,r);
        }
        else{
            sum+=QueryTree(i,l,node[i].right);
        }
    }
    i+=1;
    if(r>=node[i].left){
        //说明部分包含右子树
        if(l>=node[i].left){
            //说明全包含在右子树。
            sum+=QueryTree(i,l,r);
        }
        else{
            sum+=QueryTree(i,node[i].left,r);
        }
    }
    return sum; //返回求得的区间和。
}
int main(){
    //freopen("in.txt", "r", stdin);//提交的时候要注释掉
    ios::sync_with_stdio(false);//打消iostream中输入输出缓存,节省时间。
    cin.tie(0); cout.tie(0);//可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。
    int n,t,g;//t组数据,n个军营。
    while(cin>>t){
        rep(i,1,t){
            cin>>n;
            BuildTree(1,1,n);
            rep(j,1,n){
                cin>>g;
                node[father[j]].value=g;
                UpdateTree(father[j]);
            }
            string op;//存储输入的指令
            int a,b;
            cout<<"Case "<<i<<":\n";
            while(1){
                cin>>op;
                if(op=="End")
                    break;
                cin>>a>>b;
                if(op=="Query"){
                    cout<<QueryTree(1,a,b)<<endl;
                }
                else if(op=="Add"){
                    node[father[a]].value+=b;
                    UpdateTree(father[a]);
                }
                else if(op=="Sub"){
                    node[father[a]].value-=b;
                    UpdateTree(father[a]);
                }
            }
        }
    }
    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
发出的红包

打赏作者

unique-pure

你的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值