线段树(用结构体实现)//省空间可以用传递参数来实现 HDU 1161

/*
应用
HDU 1161
Problem 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的责骂的.




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以内。




*/
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#include<iostream>
#include<string>
#include <set>
using namespace std;
typedef long long ll;
const int maxn=30010;
int n,m;
struct node//定义结构体存储线段树
{
    int l;//区间左端点
    int r;//区间右端点
    ll ma;//区间最大值
    ll sum;//区间和
    ll lazy;//懒惰标记
    ll mark;
} tree[6*maxn];
void pushup(int k)//求和与最大值
{
    tree[k].sum=tree[k<<1].sum+tree[(k<<1)+1].sum;//将子节点的和更新到当前节点
    tree[k].ma=max(tree[k<<1].ma,tree[(k<<1)+1].ma);//将子节点的最值更新到当前节点
}
void pushdown(int k,int length)//将当前节点的信息更新到它的子节点
{
    if(tree[k].lazy)//如果当前区间懒惰标记不为0则需要更新
    {
        tree[k<<1].lazy+=tree[k].lazy;//子节点继承它的懒惰标记
        tree[(k<<1)+1].lazy+=tree[k].lazy;//子节点继承它的懒惰标记
        tree[k<<1].sum+=tree[k].lazy*(length-(length>>1));//子节点继承它的和
        tree[(k<<1)+1].sum+=tree[k].lazy*(length>>1);//子节点继承它的和
        tree[k].lazy=0;//将它的懒惰标记变为0,防止再次操作
    }
    if(tree[k].mark)//区间整段修改为同一个值
    {
        tree[k<<1].mark=tree[k].mark;
        tree[(k<<1)+1].mark=tree[k].mark;
        tree[k].mark=0;
        tree[k<<1].sum=tree[k].mark*(length-(length>>1));//子节点继承它的和
        tree[(k<<1)+1].sum=tree[k].mark*(length>>1);//子节点继承它的和
    }
}
void init(int l,int r,int k)
{
    tree[k].l=l;
    tree[k].r=r;
    tree[k].lazy=0;
    tree[k].ma=0;
    tree[k].mark=0;
    if(l==r)//到达叶子节点
    {
        scanf("%lld",&tree[k].sum);//输入叶子节点的值
        tree[k].ma=tree[k].sum;//叶子节点的最大值及其和等于它本身
        return ;//结束
    }
    int mid=(l+r)/2;
    init(l,mid,k<<1);//递归建左子树
    init(mid+1,r,(k<<1)+1);//递归建右子树
    pushup(k);//更新父节点
}
ll query(int l,int r,int k)//查询【l,r】区间的和
{
    if(l<=tree[k].l&&r>=tree[k].r)
    {
        return tree[k].sum;//如果完全包含直接返回当前区间的和
    }
    pushdown(k,tree[k].r-tree[k].l+1);//下推才能保证返回的和是正确的
    int mid=(tree[k].l+tree[k].r)/2;
    long long  res=0;
    if(l<=mid) res+=query(l,r,(k<<1));//递归加上符合条件左子树的和
    if(r>mid) res+=query(l,r,(k<<1)+1);//递归加上符合条件右子树的和
    return res;
}
ll queryma(int l,int r,int k)//查询最大值
{
    if(l<=tree[k].l&&r>=tree[k].r)
    {
        return tree[k].ma;//返回当前区间的最值
    }
    pushdown(k,tree[k].r-tree[k].l+1);//下推才能保证返回的和是正确的
    int mid=(tree[k].l+tree[k].r)/2;
    long long  resa=0;
    long long  resb=0;
    if(l<=mid) resa=queryma(l,r,(k<<1));//递归查询符合条件左子树的最值
    if(r>mid) resb=queryma(l,r,(k<<1)+1);//递归查询符合条件右子树的最值
    return max(resa,resb);
}
void update(int l,int r,int c,int k)//区间加上c
{
    if(l<=tree[k].l&&r>=tree[k].r)//如果完全包含直接更新
    {
        tree[k].lazy+=c;//lazy加上c
        tree[k].sum+=(c*(tree[k].r-tree[k].l+1));//和加上c
        tree[k].ma+=c;//最值加上c
        return ;
    }
    pushdown(k,tree[k].r-tree[k].l+1);
    int mid=(tree[k].l+tree[k].r)/2;
    if(l<=mid) update(l,r,c,k<<1);//递归更新符合条件左子树
    if(r>mid) update(l,r,c,(k<<1)+1);//递归更新符合条件右子树
    pushup(k);
}

void change(int L,int R,int c,int l,int r,int rt)  //整段区间改为某一个值
{
	if(L <= l && r <= R)
	{
		tree[rt].mark = c;//如果完全包含直接更新
		tree[rt].sum = (r - l + 1) * c;
	}
	else
	{
		pushdown(rt,tree[rt].r-tree[rt].l+1);
		int m = (l + r) / 2;
		if(L <= m)
		change(L,R,c,l,m,rt<<1);//递归更新符合条件左子树
		if(R > m)
		change(L,R,c,m+1,r,(rt<<1)+1);//递归更新符合条件右子树
		pushup(rt);
	}
}
int main()
{
    int t;
    cin>>t;//操作数
    int kase=1;
    while(t--)
    {
        int n;
        scanf("%d", &n);
        init(1, n, 1);//建树,同时输入节点的值,也就是兵营的人数
        char op[10];
        int t1, t2;

        printf("Case %d:\n", kase);
        while(scanf("%s", op))
        {
            if(op[0] == 'E')
                break;
            scanf("%d %d", &t1, &t2);
            if(op[0] == 'A')
            {
                update(t1,t1,t2,1);
            }
            else if(op[0] == 'S')
            {
                update(t1,t1,-t2,1);
            }
            else
            {
                printf("%d\n", query(t1,t2,1));
            }
        }
        kase++;
    }

}

猜你喜欢

转载自blog.csdn.net/qq_42193011/article/details/82387106