线段树更新单个节点hdu 1166 敌兵布阵

第一种类型:更新单个节
输入:第一行一个整数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条命令
输出:对第i组数据,首先输出“Case i:”和回车,
对于每个Query询问,输出一个整数并回车,表示询问的段中的总人数,这个数保持在int以内。
样例输入:
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
输出:
Case 1:
6
33
59
解题思路:刚开始看的时候感觉好简单的,随便模拟就过了吧,仔细一看时间要求模拟肯是要超时的,果断放弃,想着用线段树来写,以前只是听过还没有用它来写过题,首先是构树的过程,这个题比较简单只需要在左右端点的基础上加一个变量来求所求区间的和就可以了,具体过程看代码的注释。

#include <iostream>
#include <cstdio>
#define N 50005
using namespace std;

struct SeTree//定义线段树,左右区间可求和(key)
{
    int lchild,rchild;
    int key;
};
SeTree st[4*N];//大小最好是所给节点数的四倍
void CreatST(int l,int r,int k)
{
    st[k].lchild=l;
    st[k].rchild=r;
    if(l==r)//到叶子节点时给关建字赋值
    {
        scanf("%d",&st[k].key);
        return;
    }

    int m=(l+r)>>1;
    CreatST(l,m,k<<1);//递归构建左右子树
    CreatST(m+1,r,(k<<1)+1);
    st[k].key=st[k<<1].key+st[(k<<1)+1].key;//更新关键字的值
}

void update(int p,int add,int l,int r,int k)//p是指区间的值,add指需要加上或减去的值,l r指区间的左右端点,k是指第几个节点
{
    if(l <= p && p <= r)//找到p所在的区间,进行更新
    {
        st[k].key += add;
    } else return;//否则就返回
    if(l == r) return;//找到叶子节点就返回
    int m = (l+r)>>1;//当前区间的中间值
    if(p <= m)//小于中间值往左子树找
        update(p,add,st[k<<1].lchild,st[k<<1].rchild,k<<1);
    else//否则往右子树找
        update(p,add,st[k<<1|1].lchild,st[k<<1|1].rchild,k<<1|1);
}
int Query(int L,int R,int k)//l r为所在区间,k为第几个节点 
{
    if(L <= st[k].lchild && st[k].rchild <= R)//如果在区间内直接返回它的和
        return st[k].key;
    if(R <= st[2 * k].rchild)//最大端点小于当前节点的左子树的右端点就直接往左搜
        return Query(L, R, 2 * k);
    else if(L >= st[2 * k + 1].lchild) {//同理直接往右搜
        return Query(L, R, 2 * k + 1);
    }else {//否则两边搜
        int mid = (st[k].lchild + st[k].rchild) / 2;
        return Query(L, mid, 2 * k) + Query(mid + 1, R, 2 * k + 1);
    }

}
int main()
{
    int T,c=0;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;printf("Case %d:\n",++c);
        CreatST(1,n,1);
        char str[10];
        while(scanf("%s", str))
        {
            int x,y;
            if(str[0]=='E')
                break;
            scanf("%d %d", &x, &y);
            if(str[0]=='Q')
                printf("%d\n",Query(x,y,1));
            if(str[0]=='A')
                update(x,y,1,n,1);
            if(str[0]=='S')
                update(x,-y,1,n,1);//这里有个小技巧,减一个数等于加上它的相反数
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值