P8868 [NOIP2022] 比赛 题解

发布时间 2023-12-28 15:29:15作者: 下蛋爷

Description

小 N 和小 O 会在 2022 年 11 月参加一场盛大的程序设计大赛 NOIP!小 P 会作为裁判主持竞赛。小 N 和小 O 各自率领了一支 \(n\) 个人的队伍,选手在每支队伍内都是从 \(1\)\(n\) 编号。每一个选手都有相应的程序设计水平。具体的,小 N 率领的队伍中,编号为 \(i\)\(1 \leq i \leq n\))的选手的程序设计水平为 \(a _ i\);小 O 率领的队伍中,编号为 \(i\)\(1 \leq i \leq n\))的选手的程序设计水平为 \(b _ i\)。特别地,\(\{a _ i\}\)\(\{b _ i\}\) 还分别构成了从 \(1\)\(n\) 的排列。

每场比赛前,考虑到路途距离,选手连续参加比赛等因素,小 P 会选择两个参数 \(l, r\)\(1 \leq l \leq r \leq n\)),表示这一场比赛会邀请两队中编号属于 \([l, r]\) 的所有选手来到现场准备比赛。在比赛现场,小 N 和小 O 会以掷骰子的方式挑选出参数 \(p, q\)\(l \leq p \leq q \leq r\)),只有编号属于 \([p, q]\) 的选手才能参赛。为了给观众以最精彩的比赛,两队都会派出编号在 \([p, q]\) 内的、程序设计水平值最大的选手参加比赛。假定小 N 派出的选手水平为 \(m _ a\),小 O 派出的选手水平为 \(m _ b\),则比赛的精彩程度为 \(m _ a \times m _ b\)

NOIP 总共有 \(Q\) 场比赛,每场比赛的参数 \(l, r\) 都已经确定,但是 \(p, q\) 还没有抽取。小 P 想知道,对于每一场比赛,在其所有可能的 \(p, q\)\(l \leq p \leq q \leq r\))参数下的比赛的精彩程度之和。由于答案可能非常之大,你只需要对每一场答案输出结果对 \(2 ^ {64}\) 取模的结果即可。

link

Solution

考虑把询问离线下来然后对 \(r\) 做扫描线。

\(x_i=\max\limits_{j=i}^{r}{a_j},y_i=\max\limits_{j=i}^{r}{b_j}\)

那么区间 \([l_0,r_0]\) 的答案就是 \(r\)\([l_0,r_0]\)\(x_{l_0}\times y_{l_0}\) 的历史版本和 \(S\)

首先可以用单调栈维护对于每个 \(r\),他能控制 \(x\) 的范围和控制 \(y\) 的范围,所以当 \(r\) 往右移一格,只会有三种操作:

  1. 将区间的 \(x\) 更改为 \(a_r\)
  2. 将区间的 \(y\) 更改为 \(a_r\)
  3. 对于 \([1,r]\) 里的所有 \(i\),让 \(S_i\)\(x_i\times y_i\)

最后需要多次查询区间 \(S\) 之和。


考虑用线段树维护,容易发现每次操作之后 \(S\) 的增量一定可以表示为:\(add_{x}\times X+add_y\times Y+add_{xy}\times XY+add_c\),其中 \(add_x,add_y,add_{xy},add_c\) 都是常数。

所以可以维护懒标记 \(tag=(set_x,set_y,add_x,add_y,add_{xy},add_c)\) 分别表示区间 \(x\) 赋值标记,区间 \(y\) 赋值标记和四个系数。

考虑怎样合并两个懒标记 \(tag_1,tag_2\)

这里需要分类讨论 \(tag_1\) 是否有 \(x\) 赋值和 \(y\) 赋值。

  1. 如果 \(x\)\(y\) 都有赋值,那么只用更新 \(add_c\)

  2. 如果 \(x\) 有,\(y\) 没有,那么可以用 \(tag_2\)\(add_{xy}\)\(add_y\) 更新 \(add_t\)\(add_x\) 更新 \(add_c\)\(x\) 没有 \(y\) 没有是同理的)。

  3. 如果 \(x\)\(y\) 都没有,那么只要将 \(tag_2\)\(add_x\) 更新 \(add_x\)\(add_y\) 更新 \(add_y\)\(add_{xy}\) 更新 \(add_xy\) 即可。

不要忘记 \(add_c\) 要直接合并。

时间复杂度:\(O((n+q)\log n)\)

Code

#include <bits/stdc++.h>

// #define int int64_t

using u64 = uint64_t;

const int kMaxN = 2.5e5 + 5;

struct Node {
  u64 sumx, sumy, sumxy, sum;

  Node() {}
  Node(u64 _sumx, u64 _sumy, u64 _sumxy, u64 _sum) : sumx(_sumx), sumy(_sumy), sumxy(_sumxy), sum(_sum) {}

  friend Node operator +(Node n1, Node n2) {
    return {n1.sumx + n2.sumx, n1.sumy + n2.sumy, n1.sumxy + n2.sumxy, n1.sum + n2.sum};
  }
} t[kMaxN * 4];

struct Tag {
  u64 setx, sety, addx, addy, addxy, addc; // x 的赋值,y 的赋值,x 对 sum 的系数,y 对 sum 的系数,xy 对 sum 的系数,常数对 sum 的系数

  Tag() {}
  Tag(u64 _setx, u64 _sety, u64 _addx, u64 _addy, u64 _addxy, u64 _addc) : setx(_setx), sety(_sety), addx(_addx), addy(_addy), addxy(_addxy), addc(_addc) {}

  friend Tag operator +(Tag t1, Tag t2) { // t1:前面的,t2:后面的
    Tag ret = t1;
    if (t2.setx) ret.setx = t2.setx;
    if (t2.sety) ret.sety = t2.sety;
    if (t1.setx) {
      ret.addc += t1.setx * t2.addx;
      if (t1.sety) ret.addc += t1.sety * t2.addy + t1.setx * t1.sety * t2.addxy;
      else ret.addy += t2.addy + t1.setx * t2.addxy;
    } else {
      ret.addx += t2.addx;
      if (t1.sety) ret.addx += t1.sety * t2.addxy, ret.addc += t1.sety * t2.addy;
      else ret.addy += t2.addy, ret.addxy += t2.addxy;
    }
    ret.addc += t2.addc;
    return ret;
  }
} tag[kMaxN * 4];

int n, q;
int a[kMaxN], b[kMaxN], la[kMaxN], lb[kMaxN], stka[kMaxN], stkb[kMaxN];
u64 ans[kMaxN];
std::vector<std::pair<int, int>> vec[kMaxN];

Node merge(Node a, Tag t, int len) {
  Node ret = a;
  if (t.setx) ret.sumx = t.setx * len;
  if (t.sety) ret.sumy = t.sety * len;

  if (t.setx && t.sety) ret.sumxy = t.setx * t.sety * len;
  else if (t.setx) ret.sumxy = t.setx * a.sumy;
  else if (t.sety) ret.sumxy = t.sety * a.sumx;
  
  ret.sum += a.sumx * t.addx + a.sumy * t.addy + a.sumxy * t.addxy + t.addc * len;
  return ret;
}

struct SGT {
  Node t[kMaxN * 4];
  Tag tag[kMaxN * 4];

  void pushup(int x) {
    t[x] = t[x << 1] + t[x << 1 | 1];
  }

  void addtag(int x, int l, int r, Tag tg) {
    t[x] = merge(t[x], tg, r - l + 1), tag[x] = tag[x] + tg;
  }

  void pushdown(int x, int l, int r) {
    if (!tag[x].setx && !tag[x].sety && !tag[x].addx && !tag[x].addy && !tag[x].addxy && !tag[x].addc) return;
    int mid = (l + r) >> 1;
    addtag(x << 1, l, mid, tag[x]), addtag(x << 1 | 1, mid + 1, r, tag[x]);
    tag[x] = {0, 0, 0, 0, 0, 0};
  }

  void update(int x, int l, int r, int ql, int qr, Tag t) {
    if (l > qr || r < ql) {
      return;
    } else if (l >= ql && r <= qr) {
      return addtag(x, l, r, t);
    }
    pushdown(x, l, r);
    int mid = (l + r) >> 1;
    update(x << 1, l, mid, ql, qr, t), update(x << 1 | 1, mid + 1, r, ql, qr, t);
    pushup(x);
  }

  u64 query(int x, int l, int r, int ql, int qr) {
    if (l > qr || r < ql) {
      return 0;
    } else if (l >= ql && r <= qr) {
      return t[x].sum;
    }
    pushdown(x, l, r);
    int mid = (l + r) >> 1;
    return query(x << 1, l, mid, ql, qr) + query(x << 1 | 1, mid + 1, r, ql, qr);
  }
} sgt;

void dickdreamer() {
  int _case;
  std::cin >> _case >> n;
  for (int i = 1; i <= n; ++i) std::cin >> a[i];
  for (int i = 1; i <= n; ++i) std::cin >> b[i];
  std::cin >> q;
  for (int i = 1; i <= q; ++i) {
    int l, r;
    std::cin >> l >> r;
    vec[r].emplace_back(l, i);
  }
  int topa = 0, topb = 0;
  for (int i = 1; i <= n; ++i) {
    for (; topa && a[stka[topa]] < a[i]; --topa) {}
    for (; topb && b[stkb[topb]] < b[i]; --topb) {}
    la[i] = stka[topa], lb[i] = stkb[topb];
    stka[++topa] = i, stkb[++topb] = i;
    sgt.update(1, 1, n, la[i] + 1, i, {a[i], 0, 0, 0, 0, 0});
    sgt.update(1, 1, n, lb[i] + 1, i, {0, b[i], 0, 0, 0, 0});
    sgt.update(1, 1, n, 1, i, {0, 0, 0, 0, 1, 0});
    for (auto p : vec[i])
      ans[p.second] = sgt.query(1, 1, n, p.first, i);
  }
  for (int i = 1; i <= q; ++i)
    std::cout << ans[i] << '\n';
}

int32_t main() {
#ifdef ORZXKR
  freopen("in.txt", "r", stdin);
  freopen("out.txt", "w", stdout);
#endif
  std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
  int T = 1;
  // std::cin >> T;
  while (T--) dickdreamer();
  // std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
  return 0;
}