shuoj 风力检测 线段树

风力观测
发布时间: 2017年7月9日 20:20 最后更新: 2017年7月10日 21:12 时间限制: 1000ms 内存限制: 128M

描述
小Y正在观测y地区的风力情况,他在一条直线上依此设定了n个观测点,并观测与直线垂直方向的风力值,风力有时是正向的也有时是反向的,规定正向时的风力值为正数,他发现每次风力值的变化都可以表示为观测点上一条线段[L,R]上的同时增强或者减弱。小Y希望能够实时统计这些观测点的数据,并且实时分析这些观测点在历史中到达的风力最大绝对值,但是他无法同时对大量的观测点进行分析, 更重要的是他记不住这些观测点过去的风力大小,于是他希望你来用计算机帮助他完成这个任务。

你简化了这个问题,将问题分为两种查询:

1.对观测点[L,R]上的风力正向增强X。(X为负数表示正向减弱,即反向加强)

2.查询观测点A上的历史风力最大绝对值。

输入
第一行有一个整数T表示数据组数。(T<=10)
接着有T组数据,每组数据第一行是整数n和q,表示观测点个数和查询次数。
第二行有n个数a1,…,an,表示每个观测点的风力初始值。
接着有q行,表示q次操作,格式为:
1 L R X:表示对[L,R]线段上的正向风力同时增强x。
2 A:表示查询A点的历史风力最大绝对值。
1<=n,q<=100000。
1<=L,R,A<=n
−10000<=ai, X<=10000
输出
对每次询问2,输出一个数字表示风力值并换行。

样例输入1 复制
1
5 6
1 -1 2 3 -3
1 1 5 1
2 1
2 2
1 2 4 -5
2 2
2 3
样例输出1
2
1
5
3

这题的难点在于如何向下更新改变的值,不能直接改变下放,这样会影响时间顺序,但是可以每次下放的时候先下放最大最小值,然后这个父区间的最大最小值,就是历史的对子区间影响最大的最大最小值,下放到子区间就是对子区间最大的改变,也就是 x是tr的子节点,tr[x].minn=tr[rt].minn+tr[x].now,这就是历史最大改变值。。maxx也一样,更新完以后就可以更新 now的值了 tr[x].now+=tr[rt].now; 这就消除了要提前下放的影响,因为这样已经把提前能到的最大最小值存起来,最后要把父节点的最大最小和当前值都清空。不然多次下放怎么办。= =(更新的l,r是不用变的,我傻逼了,一直wa)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=100100;
struct node
{
    int minn,maxx,now;
}tr[maxn<<2];


struct EE
{
    int l,r,nu;
}E[maxn<<2];

int num[maxn];

void pushdown(int x)
{
    tr[x<<1].maxx=max(tr[x<<1].maxx,tr[x].maxx+tr[x<<1].now);
    tr[x<<1].minn=min(tr[x<<1].minn,tr[x].minn+tr[x<<1].now);
    tr[x<<1|1].maxx=max(tr[x<<1|1].maxx,tr[x].maxx+tr[x<<1|1].now);
    tr[x<<1|1].minn=min(tr[x<<1|1].minn,tr[x].minn+tr[x<<1|1].now);
    tr[x<<1].now+=tr[x].now;
    tr[x<<1|1].now+=tr[x].now;
    tr[x].maxx=tr[x].minn=tr[x].now=0;
}


void build(int rt,int l,int r)
{
    tr[rt].minn=0;
    tr[rt].maxx=tr[rt].now=0;
    E[rt].l=l;
    E[rt].r=r;
    if(l==r)
    {
        E[rt].nu=num[l];
        return ;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
}


void update(int rt,int l,int r,int b)
{
    if(E[rt].l>=l&&E[rt].r<=r)
    {
        tr[rt].now+=b;
        tr[rt].minn=min(tr[rt].minn,tr[rt].now);
        tr[rt].maxx=max(tr[rt].maxx,tr[rt].now);
        return ;
    }
    pushdown(rt);
    int mid=(E[rt].l+E[rt].r)>>1;
    if(l<=mid)  update(rt<<1,l,r,b);
    if(r>mid) update(rt<<1|1,l,r,b);
}

int query(int rt,int d)
{
    if(E[rt].l==E[rt].r&&E[rt].l==d)
    {
        return max(abs(E[rt].nu+tr[rt].maxx),abs(E[rt].nu+tr[rt].minn));
    }
      pushdown(rt);
    int mid=(E[rt].l+E[rt].r)>>1;
    if(d<=mid)  return query(rt<<1,d);
    if(d>mid) return query(rt<<1|1,d);
}

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,q;
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&num[i]);
        }
        build(1,1,n);
        while(q--)
        {
            int d;
            scanf("%d",&d);
            if(d==1)
            {
                int l,r,x;
                scanf("%d%d%d",&l,&r,&x);
                update(1,l,r,x);
            }
            else
            {
                int x;
                scanf("%d",&x);
                printf("%d\n",query(1,x));
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值