kuangbin 线段树 - HDU - 1166 敌兵布阵 (线段树单点修改模板题)

kuangbin 线段树 - HDU - 1166 敌兵布阵 (线段树单点修改模板题)

总题单 week 3 [kuangbin带你飞] 题单 最小生成树 + 线段树 Click here ~~
https://blog.csdn.net/m0_46272108/article/details/108980362

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<=50000) NN<=50000,表示敌人有N个工兵营地,接下来有N个正整数,第i个正整数 a i a_i ai 代表第i个工兵营地里开始时有 a i a_i ai 个人 ( 1 < = a i < = 50 ) (1<=ai<=50) 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/m0_46272108/article/details/108955623

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<cmath>
#include<map>
#include<algorithm>

#define IOS ios::sync_with_stdio(false); cin.tie(0); cout.tie(0)
#define ll long long
//#define int ll
#define inf 0x3f3f3f3f
using namespace std;
int read()
{
	int w = 1, s = 0;
	char ch = getchar();
	while (ch < '0' || ch>'9') { if (ch == '-') w = -1; ch = getchar(); }
	while (ch >= '0' && ch <= '9') { s = s * 10 + ch - '0';    ch = getchar(); }
	return s * w;
//最大公约数
}int gcd(int x,int y) {
    if(x<y) swap(x,y);//很多人会遗忘,大数在前小数在后
    //递归终止条件千万不要漏了,辗转相除法
    return x % y ? gcd(y, x % y) : y;
}
int lcm(int x,int y)//计算x和y的最小公倍数
{
    return x * y / gcd(x, y);//使用公式
}
//------------------------ 以上是我常用模板与刷题几乎无关 ------------------------//
const int N = 50010;
int num[N * 4];	//存储每一个工兵营的人数

struct Node{
    int l, r;//左端点,右端点
    int val;//区间[l, r]的最大值
}tr[N << 2];


//由子节点的信息来计算父节点的信息
void pushup(int cur){
    //父节点是两个子节点的和,一直往上传值,就能使根节点是所有人的个数
    tr[cur].val = tr[cur << 1].val + tr[cur << 1 | 1].val;
}

//cur代表当前节点,
void build(int cur, int l, int r){
    
    //建树的初始值
    tr[cur].l = l, tr[cur].r = r, tr[cur].val = 0;
    //如果已经是叶结点return
    if(l == r) {
        tr[cur].val = num[l];//如果到达叶节点就把人数赋给该节点
        return;
    }
        
    //否则求一下当前区间的中点
    int mid = l + r >> 1;
    //递归建立左边区间
    build(cur << 1, l, mid);
    //递归建立右边区间
    build(cur << 1 | 1, mid + 1, r);
    
    //将子节点的人数进行汇总,给父节点
    pushup(cur);
}

//[l, r]查询区间   cur代表当前线段树里面的端点。
int query(int cur, int ql, int qr)  {
    int l = tr[cur].l, r = tr[cur].r;
    //①情况[TL,TR] ? [L,R]
    //树中节点,已经被完全包含在[l, r]中了。
    if(ql <= l && qr >= r)  
        return tr[cur].val;

    int mid = l + r >> 1;
    int val = 0;
    
    //判断与左边有交集
    if (ql <= mid) {
        val += query(cur << 1, ql, qr);//求和
    }
        
    
    //这里为什么是 qr > mid,因为划分的区间是[l, mid][mid + 1, r],所以要用>而不能=
    //判断与右边有交集
    if (qr > mid) {
        val += query(cur << 1 | 1, ql, qr);//求和
    }
    //返回结果
    return val;
}

//cur代表当前线段树里面的端点。tar代表要修改的位置,即目标位置
void modify(int cur, int tar, int val) {
    
    int l = tr[cur].l, r = tr[cur].r;
    //如果当前节点就是叶节点,那么直接修改就可以了
    if (l == r) {
        tr[cur].val = tr[cur].val + val;
        return;
    }
    int mid = l + r >> 1;
    if (tar <= mid) {
        modify (cur << 1, tar, val);
    } else {
        modify (cur << 1 | 1, tar, val);
    }
    //递归完之后,要更新到父节点。
    //pushup就是更新父节点的信息
    pushup(cur);    
}

int main()
{
    int t = read();
    for(int i = 1; i <= t; i++)
    {
        int n = read();
        for(int j = 1; j <= n; j++) 
            num[j] = read();
        //建树
        build(1, 1, n);
        int flag = 1;
        while (1) {
            string str;
            cin >> str;
            //Case 1:
            if (flag) {
                printf("Case %d:\n", i);
                flag = 0;
            }
            //询问
            if (str == "Query") {
                int ql = read(), qr = read();
                printf("%d\n", query(1, ql, qr));
            }
            //添加
            if (str == "Add") {
                int c = read(), m = read();
                modify(1, c, m);
            }
            //减去
            if (str == "Sub") {
                int c = read(), m = read();
                modify(1, c, -m);
            }
            //结束
            if (str == "End")
                break;
        }
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值