首页 > 其他分享 >P8518 [IOI2021] 分糖果 题解

P8518 [IOI2021] 分糖果 题解

时间:2024-08-16 16:18:29浏览次数:16  
标签:std le 盒子 int 题解 kMaxN IOI2021 糖果 P8518

Description

Khong 阿姨正在给附近一所学校的学生准备 \(n\) 盒糖果。盒子的编号分别为 \(0\) 到 \(n - 1\),开始时盒子都为空。第 \(i\) 个盒子 \((0 \leq i \leq n - 1)\) 至多可以容纳 \(c[i]\) 块糖果(容量为 \(c[i]\))。

Khong 阿姨花了 \(q\) 天时间准备糖果盒。在第 \(j\) 天 \((0 \leq j \leq q - 1)\),她根据三个整数 \(l[j]\)、 \(r[j]\) 和 \(v[j]\) 执行操作,其中 \(0 \leq l[j] \leq r[j] \leq n - 1\) 且 \(v[j] \neq 0\)。对于每个编号满足 \(l[j] \leq k \leq r[j]\) 的盒子 \(k\):

  • 如果 \(v[j] > 0\),Khong 阿姨将糖果一块接一块地放入第 \(k\) 个盒子,直到她正好放了 \(v[j]\) 块糖果或者该盒子已满。也就是说,如果该盒子在这次操作之前已有 \(p\) 块糖果,那么在这次操作之后盒子将有 \(\min(c[k], p + v[j])\) 块糖果。

  • 如果 \(v[j] < 0\),Khong 阿姨将糖果一块接一块地从第 \(k\) 个盒子取出,直到她正好从盒子中取出 \(-v[j]\) 块糖果或者该盒子已空。也就是说,如果该盒子在这次操作之前已有 \(p\) 块糖果,那么在这次操作之后盒子将有 \(\max(0, p + v[j])\) 块糖果。

你的任务是求出 \(q\) 天之后每个盒子中糖果的数量。

约束条件

  • \(1 \le n \le 200 000\)

  • \(1 \le q \le 200 000\)

  • \(1 \le c[i] \le 10 ^ 9\) (对所有 \(0 \le i \le n - 1\))

  • \(0 \le l[j] \le r[j] \le n - 1\)(对所有 \(0 \le j \le q - 1\))

  • \(−10 ^ 9 \le v[j] \le 10 ^ 9\) , \(v[j] ≠ 0\)(对所有 \(0 \le j \le q - 1\))

Solution

不妨设 \(a_i\) 表示当前 \(i\) 的糖果数量,那么每次操作就是让 \(a_i\leftarrow\max\left(\min\left(a_i+v,c_i\right),0\right)\),容易发现可以扫描线然后维护一个关于时间轴的线段树。

如果取 min 和 max 中只有一种是很好做的,就是这题,但是两个放在一起就不好做了。

容易发现只要找到最小的后缀 \([p,q]\) 使得这个后缀同时存在 min 和 max,那么 \([p+1,q]\) 就只有一种 min 或者 max 了,所以确定 \(p\) 时刻的值就可以通过 \([p+1,q]\) 的操作得出答案。

考虑如何找到 \(p\)。

观察可知如果 \([p,q]\) 前缀和的极差 \(\geq c_i\) 就一定同时存在 max 和 min,并且如果极差 \(<c_i\) 就一定不会同时存在 max 和 min,所以只要在线段树上二分即可。

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

Code

#include "candies.h"

#include <bits/stdc++.h>

const int kMaxN = 2e5 + 5;

int n, q;
int c[kMaxN], l[kMaxN], r[kMaxN], v[kMaxN];
std::vector<std::pair<int, int>> vec[kMaxN];

struct SGT {
  int64_t mx[kMaxN * 4], mi[kMaxN * 4], posmx[kMaxN * 4], posmi[kMaxN * 4], tag[kMaxN * 4];

  void pushup(int x) {
    if (mx[x << 1] > mx[x << 1 | 1]) {
      mx[x] = mx[x << 1], posmx[x] = posmx[x << 1];
    } else {
      mx[x] = mx[x << 1 | 1], posmx[x] = posmx[x << 1];
    }
    if (mi[x << 1] < mi[x << 1 | 1]) {
      mi[x] = mi[x << 1], posmi[x] = posmi[x << 1];
    } else {
      mi[x] = mi[x << 1 | 1], posmi[x] = posmi[x << 1 | 1];
    }
  }

  void addtag(int x, int64_t v) { tag[x] += v, mx[x] += v, mi[x] += v; }

  void pushdown(int x) {
    if (tag[x]) {
      addtag(x << 1, tag[x]), addtag(x << 1 | 1, tag[x]);
      tag[x] = 0;
    }
  }

  void build(int x, int l, int r) {
    mx[x] = mi[x] = tag[x] = 0, posmx[x] = posmi[x] = r;
    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);
    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 getpos(int x, int l, int r, int64_t nowmx, int64_t nowmi, int64_t v) {
    if (std::max(nowmx, mx[x]) - std::min(nowmi, mi[x]) < v || l == r) return l;
    pushdown(x);
    int mid = (l + r) >> 1;
    if (std::max(nowmx, mx[x << 1 | 1]) - std::min(nowmi, mi[x << 1 | 1]) >= v)
      return getpos(x << 1 | 1, mid + 1, r, nowmx, nowmi, v);
    else 
      return getpos(x << 1, l, mid, std::max(nowmx, mx[x << 1 | 1]), std::min(nowmi, mi[x << 1 | 1]), v);
  }

  std::pair<int64_t, int> querymin(int x, int l, int r, int ql, int qr) {
    if (l > qr || r < ql) return {1e18, -1};
    else if (l >= ql && r <= qr) return {mi[x], posmi[x]};
    pushdown(x);
    int mid = (l + r) >> 1;
    return std::min(querymin(x << 1, l, mid, ql, qr), querymin(x << 1 | 1, mid + 1, r, ql, qr));
  }

  std::pair<int64_t, int> querymax(int x, int l, int r, int ql, int qr) {
    if (l > qr || r < ql) return {-1e18, -1};
    else if (l >= ql && r <= qr) return {mx[x], posmx[x]};
    pushdown(x);
    int mid = (l + r) >> 1;
    return std::max(querymax(x << 1, l, mid, ql, qr), querymax(x << 1 | 1, mid + 1, r, ql, qr));
  }
} sgt;

std::vector<int> solve() {
  std::vector<int> ans;
  for (int i = 1; i <= q; ++i) {
    vec[l[i]].emplace_back(i, v[i]), vec[r[i] + 1].emplace_back(i, -v[i]);
  }
  sgt.build(1, 0, q);
  for (int i = 1; i <= n; ++i) {
    for (auto [x, v] : vec[i]) sgt.update(1, 0, q, x, q, v);
    int p = sgt.getpos(1, 0, q, -1e18, 1e18, c[i]);
    int64_t sump = sgt.querymin(1, 0, q, p, p).first;
    if (p && sump == sgt.querymin(1, 0, q, p, q).first) { // 后面只会顶上界
      auto [mx, id] = sgt.querymax(1, 0, q, p, q);
      if (mx - sump <= c[i]) ans.emplace_back(sgt.querymax(1, 0, q, q, q).first - sump);
      else ans.emplace_back(c[i] - (mx - sgt.querymax(1, 0, q, q, q).first));
    } else if (p) {
      auto [mi, id] = sgt.querymin(1, 0, q, p, q);
      if (sump - mi <= c[i]) ans.emplace_back(c[i] - (sump - sgt.querymin(1, 0, q, q, q).first));
      else ans.emplace_back(sgt.querymin(1, 0, q, q, q).first - mi);
    } else if (sgt.querymin(1, 0, q, 0, q).first >= 0) {
      auto [mx, id] = sgt.querymax(1, 0, q, p, q);
      if (mx <= c[i]) ans.emplace_back(sgt.querymax(1, 0, q, q, q).first);
      else ans.emplace_back(c[i] - (mx - sgt.querymax(1, 0, q, q, q).first));
    } else {
      auto [mi, id] = sgt.querymin(1, 0, q, p, q);
      if (mi >= 0) ans.emplace_back(sgt.querymin(1, 0, q, q, q).first);
      else ans.emplace_back(sgt.querymin(1, 0, q, q, q).first - mi);
    }
  }
  return ans;
}

std::vector<int> distribute_candies(std::vector<int> c, std::vector<int> l,
                                    std::vector<int> r, std::vector<int> v) {
  n = c.size(), q = l.size();
  for (int i = 0; i < n; ++i) ::c[i + 1] = c[i];
  for (int i = 0; i < q; ++i)
    ::l[i + 1] = l[i] + 1, ::r[i + 1] = r[i] + 1, ::v[i + 1] = v[i];
  return solve();
}

标签:std,le,盒子,int,题解,kMaxN,IOI2021,糖果,P8518
From: https://www.cnblogs.com/Scarab/p/18363077

相关文章

  • [CEOI2009] photo 题解
    前言题目链接:洛谷。好多错解都被我叉了,所以来贡献一发正确的题解,并予以解释。题意简述平面上有\(n\)个点,现在要求用最少的底边在\(x\)轴上且面积小于等于\(S\)的矩形覆盖所有点,这些矩形可以重叠。问最少矩形数量。矩形顶点不必是整点。\(n\leq100\)。题目分析没什......
  • P10238 [yLCPC2024] F. PANDORA PARADOXXX 题解
    题目传送门前置知识树链剖分|树的直径|最近公共祖先|并查集解法正着删边不太可做,考虑离线下来反着加边。一个显而易见的结论:设点集\(A\)的直径的两个端点为\(u_{1},v_{1}\),另一个点集\(B\)的直径的两个端点为\(u_{2},v_{2}\),则\(A\bigcupB\)的直径端点一定是......
  • 启动应用程序出现pcsvDevice.dll找不到问题解决
    其实很多用户玩单机游戏或者安装软件的时候就出现过这种问题,如果是新手第一时间会认为是软件或游戏出错了,其实并不是这样,其主要原因就是你电脑系统的该dll文件丢失了或没有安装一些系统软件平台所需要的动态链接库,这时你可以下载这个pcsvDevice.dll文件(挑选合适的版本文件)把......
  • P6109 [Ynoi2009] rprmq1 题解
    Description有一个\(n\timesn\)的矩阵\(a\),初始全是\(0\),有\(m\)次修改操作和\(q\)次查询操作,先进行所有修改操作,然后进行所有查询操作。一次修改操作会给出\(l_1,l_2,r_1,r_2,x\),代表把所有满足\(l_1\lei\ler_1\)且\(l_2\lej\ler_2\)的\(a_{i,j}\)元......
  • 题解: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\)轮至第\(......