“盛大游戏杯”第15届上海大学程序设计联赛夏季赛暨上海高校金马五校赛 M SHUOJ 422 风力观测

文章的标题为什么要取得这么长?(逃

题目地址

http://acmoj.shu.edu.cn/problem/422/

题意

给定一个数 n(1<=n<=105) ,以及区间 [1,n] 上每个数的初始值,接下来有 q(1<=q<=105) 次操作,操作有两种类型,格式分别为:

  1. 1 L R X,表示对 [L,R] 区间上所有数加 x(104<=x<=104)
  2. 2 A,询问A点的历史上最大绝对值

思路

可以这样看问题,记 [1,n] 上初始值为 a[i] ,一开始区间 [1,n] 上的所有值都为0,然后往 [L,R] 上不断加x,有个数据结构可以保存某个区间上的历史最大值 mx 和历史最小值 mn ,于是每次询问的答案就是 max(abs(a[i]+mx[i]),abs(a[i]+mn[i]))

很容易可以想到这个可以用线段树来保存区间上的历史最大和最小值。难点就是如何分配线段树上的各个值以及相关操作的执行方式。

laz[] 数组表示一个区间节点要向其子节点传播的修改量,用 mx[] 数组表示 laz[] 数组历史上的最大值,同理, mn[] 为最小值。那么对一个父节点 y ,其子节点x受到这样的影响:
laz[x]+=laz[y] ,区间y把自身信息向其子区间x传播。
mx[x]=max(mx[x],laz[x]+mx[y]) laz[x] 的历史最大值就是其父节点y的 laz[y] 的历史最大值对x的影响, mn[x] 同理。

特别注意的一点是:每次向下传播(pushDown())后, laz,mx,mn 这些数组都要清空,是的,这是个很基础的线段树注意点,但是本渣渣就错在了这里QAQ。因为mx和mn记录的是laz的历史最值,所以,laz被清空时,mx,mn一定要被清空,这样才能保证操作的正确性。

8.21.UPD:最近发生了好多事,人很丧,博客都几百年没更新了,这题还有种虽然比较复杂,但是更值得学习一下的做法,就是用扫描线的思想以时间建立线段树,离线按序号大小处理各个节点的变化,大致思路就是这样,详见代码。

代码

// 风力观测
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

#define PB push_back
#define lowbit(x) (x &(-x))
#define MS(x, y) memset(x, y, sizeof(x))
#define abs(x) ((x) < 0 ? -(x) : (x))
#define LL rt << 1
#define RR rt << 1 | 1
#define lson l, mid, LL
#define rson mid + 1, r, RR

typedef long long ll;
typedef pair<int, int> P;
const int MAXN = 1e5 + 5;
const int INF = 0x3f3f3f3f;

int n, q;
int mx[MAXN << 2], mn[MAXN << 2], laz[MAXN << 2];
int a[MAXN];

inline void pushDown(int rt) {
  mx[LL] = max(mx[LL], laz[LL] + mx[rt]);
  mn[LL] = min(mn[LL], laz[LL] + mn[rt]);
  mx[RR] = max(mx[RR], laz[RR] + mx[rt]);
  mn[RR] = min(mn[RR], laz[RR] + mn[rt]);
  laz[LL] += laz[rt];
  laz[RR] += laz[rt];
  mx[rt] = mn[rt] = laz[rt] = 0;
}

void build(int l, int r, int rt) {
  mx[rt] = mn[rt] = laz[rt] = 0;
  if (l == r) return ;
  int mid = (l + r) >> 1;
  build(lson);
  build(rson);
}

void update(int L, int R, int x, int l, int r, int rt) {
  if (L <= l && r <= R) {
    laz[rt] += x;
    mx[rt] = max(mx[rt], laz[rt]);
    mn[rt] = min(mn[rt], laz[rt]);
    return ;
  }
  if (mx[rt] || mn[rt] || laz[rt]) pushDown(rt);
  int mid = (l + r) >> 1;
  if (L <= mid) update(L, R, x, lson);
  if (mid < R) update(L, R, x, rson);
}

P query(int x, int l, int r, int rt) {
  if (l == r) {
    return P(mx[rt], mn[rt]);
  }
  if (mx[rt] || mn[rt] || laz[rt]) pushDown(rt);
  int mid = (l + r) >> 1;
  if (x <= mid) return query(x, lson);
  return query(x, rson);
}

int main() {
  int T;
  scanf("%d", &T);
  while (T--) {
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; ++i) scanf("%d", a + i);
    build(1, n, 1);
    int op, x, l, r;
    P tmp;
    while (q--) {
      scanf("%d", &op);
      if (op == 1) {
        scanf("%d%d%d", &l, &r, &x);
        update(l, r, x, 1, n, 1);
      } else {
        scanf("%d", &x);
        tmp = query(x, 1, n, 1);
        printf("%d\n", max(abs(a[x] + tmp.first), abs(a[x] + tmp.second)));
      }
    }
  }
}
/*
1
5 6
1 -1 2 3 -3
1 1 5 1
2 1
2 2
1 2 4 -5
2 2
2 3

*/

扫描线离线代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define ll rt << 1
#define rr rt << 1 | 1
#define lson l, mid, ll
#define rson mid + 1, r, rr
#define abs(x) ((x) < 0 ? -(x) : (x))

typedef pair<int, int> P;
const int N = 1e5 + 5;
const int INF = 0x3f3f3f3f;

struct OP {
  int pos, op, val, tim;
  bool operator<(const OP& r) const {
    return pos < r.pos || (pos == r.pos && op < r.op);
  }
};

int n, m;
int a[N], ans[N];
int tot, tol;
int mx[N << 2], mn[N << 2], laz[N << 2];
OP p[N << 1];

inline void gm(P &a, P b) {
  if (a.first < b.first) a.first = b.first;
  if (a.second > b.second) a.second = b.second;
}

inline void pushDown(int rt) {
  mx[ll] += laz[rt]; mn[ll] += laz[rt]; laz[ll] += laz[rt];
  mx[rr] += laz[rt]; mn[rr] += laz[rt]; laz[rr] += laz[rt];
  laz[rt] = 0;
}

inline void pushUp(int rt) {
  mx[rt] = max(mx[ll], mx[rr]);
  mn[rt] = min(mn[ll], mn[rr]);
}

void build(int l, int r, int rt) {
  mx[rt] = mn[rt] = laz[rt] = 0;
  if (l == r) return ;
  int mid = (l + r) >> 1;
  build(lson);
  build(rson);
}

void add(int L, int R, int V, int l, int r, int rt) {
  if (L <= l && r <= R) {
    mx[rt] += V;
    mn[rt] += V;
    laz[rt] += V;
    return ;
  }
  if (laz[rt]) pushDown(rt);
  int mid = (l + r) >> 1;
  if (L <= mid) add(L, R, V, lson);
  if (mid < R) add(L, R, V, rson);
  pushUp(rt);
}

P query(int L, int R, int l, int r, int rt) {
  if (L <= l && r <= R) {
    return P(mx[rt], mn[rt]);
  }
  if (laz[rt]) pushDown(rt);
  int mid = (l + r) >> 1;
  P ret = P(-INF, INF);
  if (L <= mid) gm(ret, query(L, R, lson));
  if (mid < R) gm(ret, query(L, R, rson));
  return ret;
}

int main() {
  int T;
  scanf("%d", &T);
  while (T--) {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i) scanf("%d", a + i);
    tol = tot = 0;
    int op, l, r, x;
    for (int i = 1; i <= m; ++i) {
      scanf("%d", &op);
      if (op == 1) {
        scanf("%d%d%d", &l, &r, &x);
        p[tot].op = 0;
        p[tot].pos = l;
        p[tot].tim = i;
        p[tot].val = x;
        ++tot;
        p[tot].op = 0;
        p[tot].pos = r + 1;
        p[tot].tim = i;
        p[tot].val = -x;
        ++tot;
      } else {
        scanf("%d", &x);
        p[tot].op = 1;
        p[tot].pos = x;
        p[tot].tim = i;
        p[tot].val = tol++;
        ++tot;
      }
    }
    sort(p, p + tot);
    build(1, m, 1);
    P ret;
    for (int i = 0; i < tot; ++i) {
      if (p[i].op) {
        ret = query(1, p[i].tim, 1, m, 1);
        ans[p[i].val] = max(max(abs(a[p[i].pos] + ret.first), abs(a[p[i].pos] + ret.second)),
                            abs(a[p[i].pos]));
      } else {
        add(p[i].tim, m, p[i].val, 1, m, 1);
      }
    }
    for (int i = 0; i < tol; ++i) printf("%d\n", ans[i]);
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值