首页 > 其他分享 >P6109 [Ynoi2009] rprmq1 题解

P6109 [Ynoi2009] rprmq1 题解

时间:2024-08-16 14:38:09浏览次数:7  
标签:Ynoi2009 le r1 int 题解 kMaxN leq rprmq1 l1

Description

有一个 \(n \times n\) 的矩阵 \(a\),初始全是 \(0\),有 \(m\) 次修改操作和 \(q\) 次查询操作,先进行所有修改操作,然后进行所有查询操作。

一次修改操作会给出 \(l_1,l_2,r_1,r_2,x\),代表把所有满足 \(l_1 \le i \le r_1\) 且 \(l_2 \le j \le r_2\) 的 \(a_{i,j}\) 元素加上一个值 \(x\)。

一次查询操作会给出 \(l_1,l_2,r_1,r_2\),代表查询所有满足 \(l_1 \le i \le r_1\) 且 \(l_2 \le j \le r_2\) 的 \(a_{i,j}\) 元素的最大值。

\(1\leq n,m\leq 5\times 10^4\),\(1\leq q \leq 5\times 10^5\),\(1\leq x\leq 2147483647\),\(1\leq l_1\leq r_1\leq n\),\(1\leq l_2\leq r_2\leq n\)。

Solution

容易发现可以扫描线,但是直接做的话查询时需要维护任意一段时刻内的区间最大值,这显然是做不了的。

但是如果所有的查询 \(l_1\) 均相等的话就可以从小到大枚举修改操作和查询的 \(r_1\),这样只需要通过区间加、历史最大值线段树维护当前所有操作的区间历史最大值。

这可以启发我们进行类似猫树分治的做法,从浅到深枚举线段树的每一层,找到所有横跨当前层不同节点的询问放在当前层做,剩下的放到更深层做。

由于横跨当前层不同节点的询问 \([l_1,r_1]\) 一定只跨过两个节点,所以这两个节点可以把它切割成 \([l_1,k]\) 和 \([k+1,r_1]\),对于 \([k+1,r_1]\) 这部分只需要维护一个历史最大值线段树,然后从小到大做修改操作,在每个线段树节点的开头重置历史最大值为当前最大值即可。\([l_1,k]\) 的部分同理。

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

Code

#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 5e4 + 5, kMaxQ = 5e5 + 5;

int n, m, q;
int l1[kMaxQ], r1[kMaxQ], l2[kMaxQ], r2[kMaxQ], ans[kMaxQ];
bool vis[kMaxQ];
std::vector<std::tuple<int, int, int, int, int>> ud;
std::vector<std::tuple<int, int, int>> qq[kMaxN], vec[kMaxN];

struct SGT {
  int maxa[kMaxN * 8], maxb[kMaxN * 8], tag1[kMaxN * 8], tag2[kMaxN * 8], tagr[kMaxN * 8];

  void pushup(int x) {
    maxa[x] = std::max(maxa[x << 1], maxa[x << 1 | 1]);
    maxb[x] = std::max(maxb[x << 1], maxb[x << 1 | 1]);
  }

  void addtag(int x, int v1, int v2) {
    maxb[x] = std::max(maxb[x], maxa[x] + v2);
    tag2[x] = std::max(tag2[x], tag1[x] + v2);
    maxa[x] += v1, tag1[x] += v1;
  }

  void reset(int x) {
    addtag(x << 1, tag1[x], tag2[x]), addtag(x << 1 | 1, tag1[x], tag2[x]);
    maxb[x] = maxa[x], tagr[x] = 1;
    tag1[x] = tag2[x] = 0;
  }

  void pushdown(int x) {
    if (tagr[x]) {
      reset(x << 1), reset(x << 1 | 1);
      tagr[x] = 0;
    }
    if (tag1[x] || tag2[x]) {
      addtag(x << 1, tag1[x], tag2[x]), addtag(x << 1 | 1, tag1[x], tag2[x]);
      tag1[x] = tag2[x] = 0;
    }
  }

  void build(int x, int l, int r) {
    maxa[x] = maxb[x] = tag1[x] = tag2[x] = tagr[x] = 0;
    if (l == r) return;
    int mid = (l + r) >> 1;
    build(x << 1, l, mid), build(x << 1 | 1, mid + 1, r);
  }

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

  int 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 maxb[x];
    pushdown(x);
    int mid = (l + r) >> 1;
    return std::max(query(x << 1, l, mid, ql, qr), query(x << 1 | 1, mid + 1, r, ql, qr));
  }
} sgt;

void solve(int d) {
  for (int i = 1; i <= n; ++i) vec[i].clear(), qq[i].clear();
  for (auto [l1, r1, l2, r2, x] : ud)
    vec[l1].emplace_back(l2, r2, x), vec[r1 + 1].emplace_back(l2, r2, -x);
  for (int i = 1; i <= q; ++i) {
    if (!vis[i] && ((l1[i] >> d) != (r1[i] >> d) || !d)) {
      if (l1[i] != r1[i]) assert((l1[i] >> d) == (r1[i] >> d) - 1);
      qq[r1[i]].emplace_back(i, l2[i], r2[i]);
    }
  }
  sgt.build(1, 1, n);
  for (int i = 1; i <= n; ++i) {
    for (auto [l, r, v] : vec[i]) {
      if (v < 0) sgt.update(1, 1, n, l, r, v);
    }
    for (auto [l, r, v] : vec[i]) {
      if (v >= 0) sgt.update(1, 1, n, l, r, v);
    }
    if (i % (1 << d) == 0) sgt.reset(1);
    for (auto [id, l, r] : qq[i]) ans[id] = std::max(ans[id], sgt.query(1, 1, n, l, r));
  }

  for (int i = 1; i <= n; ++i) vec[i].clear(), qq[i].clear();
  for (auto [l1, r1, l2, r2, x] : ud)
    vec[r1].emplace_back(l2, r2, x), vec[l1 - 1].emplace_back(l2, r2, -x);
  for (int i = 1; i <= q; ++i) {
    if (!vis[i] && ((l1[i] >> d) != (r1[i] >> d) || !d)) {
      if (l1[i] != r1[i]) assert((l1[i] >> d) == (r1[i] >> d) - 1);
      vis[i] = 1;
      qq[l1[i]].emplace_back(i, l2[i], r2[i]);
    }
  }
  sgt.build(1, 1, n);
  for (int i = n; i; --i) {
    for (auto [l, r, v] : vec[i]) {
      if (v < 0) sgt.update(1, 1, n, l, r, v);
    }
    for (auto [l, r, v] : vec[i]) {
      if (v >= 0) sgt.update(1, 1, n, l, r, v);
    }
    if (i % (1 << d) == (1 << d) - 1) sgt.reset(1);
    for (auto [id, l, r] : qq[i]) ans[id] = std::max(ans[id], sgt.query(1, 1, n, l, r));
  }
}

void dickdreamer() {
  std::cin >> n >> m >> q;
  for (int i = 1; i <= m; ++i) {
    int l1, l2, r1, r2, x;
    std::cin >> l1 >> l2 >> r1 >> r2 >> x;
    ud.emplace_back(l1, r1, l2, r2, x);
  }
  for (int i = 1; i <= q; ++i)
    std::cin >> l1[i] >> l2[i] >> r1[i] >> r2[i];
  for (int i = std::__lg(n) + 1; ~i; --i) {
    solve(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;
}

标签:Ynoi2009,le,r1,int,题解,kMaxN,leq,rprmq1,l1
From: https://www.cnblogs.com/Scarab/p/18362804

相关文章

  • 题解:P10688 Buy Tickets
    题目大意排队时有人插队。输入格式给定队列长度$n$。接下来$n$行每行两个正整数,第一个表示该元素插入位置,另一个表示该元素的权值。输出格式按照顺序输出该位置元素的权值。注意事项输入的数据组数未知,需要一直输入,输入方法可以参考以下代码。while(cin>>n){......
  • 题解:P10781 【MX-J1-T1】『FLA - III』Spectral
    P10781【MX-J1-T1】『FLA-III』Spectral题解(非正解,正解应该是数学题。)这道题很简单,分析题意就可以得出核心代码:for(inti=1;i<=n;i++){ans=k+ans/i;}那么恭喜你获得$40$pts。为什么呢?因为题目需要的是最高温度,而烧碳获得的温度可能小于烧炭时减低的温度。简单说......
  • JOISC2020 Day 4 A 首都 题解
    JOISC2020Day4A首都JOIAtCoderLuogu考虑一条链的情形。如图,将每个城市视为一条线段,容易发现交错(有交但不包含)的若干线段必须全部合并才能符合条件。但如果这么写会出错,原因是线段有包含关系,外层线段需要统计内层线段的答案,但内层线段不需要统计外层线段的答案。如果设内......
  • 题解:AT_abc365_d [ABC365D] AtCoder Janken 3
    D-AtCoderJanken3题解题意:高桥和青木要玩石头剪刀布,给你一个长度为\(n\)的字符串\(s\),\(s\)表示青木在第\(i\)局游戏中的动作(R表示石头,P表示布,S表示剪刀。)。高桥不可以在任何一局中输给青木(即:高桥和青木只可以平局或高桥赢青木),且高桥第\(i\)局出的和第\(i-1\)局......
  • 题解:P10313 [SHUPC 2024] 占地斗士!
    题目大意给出一个由.和#组成的\(n\timesm\)矩阵,然后再给你这\(4\)种图像,用着四种图像对矩阵进行覆盖(每个只能用一次)。其中,#的位置不可以被图像遮挡,也不能放在不能放置的格子上。解题思路考虑使用爆搜。第一个图像:if(mp[i][j]!='#'&&mp[i+1][j+1]!='#'......
  • 题解:P10111 [GESP202312 七级] 纸牌游戏
    题目大意给出三个序列:\(a\),\(b\),\(c\)分别表示:分数,罚分以及小杨从第\(1\)轮至第\(......
  • 题解:AtCoder Janken 3
    D-AtCoderJanken3题解题意高桥和青木要玩石头剪刀布,给你一个长度为\(n\)的字符串\(s\),\(s\)表示青木在第\(i\)局游戏中的动作(R表示石头,P表示布,S表示剪刀)。高桥不可以在任何一局中输给青木(即:高桥和青木只可以平局或高桥赢青木),且高桥第\(i\)局出的和第\(i-1\)局......
  • Codeforces 232 B Table 题解 [ 蓝 ] [ 分组背包 ] [ 组合数学 ] [ 循环节 ]
    Codeforces232BTable。蒟蒻模拟赛上场切的一道蓝,非常难以置信我竟然能做蓝题。这题的数据范围初看还是比较坑的,\(10^{18}\)的值域很容易让人往矩阵加速那方面想。实际上在列出转移方程式后,我们发现状态是二维的,无法使用矩阵加速(或者说这样做很麻烦)。思路首先观察到每个边长......
  • AT_agc025_b RGB Coloring 题解
    ProblemSolution由于涂绿色的得分为\(A+B\),所以可以将红色与蓝色独立考虑。依次枚举红色的个数,假定为\(i\),所以剩余需要的得分为\(K-i\timesA\),判断是否能被\(B\)整除,若能,则蓝色个数为\(\frac{K-i\timesA}{B}\),设为\(j\),则总方案累加\(C^{i}_{n}\timesC^{j}_{n}\),除......
  • [Ynoi2016] 镜中的昆虫 题解
    难度在最近遇到的题里相对较高,在这里写一篇珂学题解。(以下是学校给的部分分)\(20\%\):直接暴力枚举。另外\(20\%\):假如我们取\(pre\),对于\(pre<l\)的,\(ans++\),明显二维偏序,树状数组或\(cdq\)即可,时间复杂度\(O(n\logn)\)。另外\(40\%\):相当于多加一个时间维,三维偏序,\(......