首页 > 其他分享 >P7408 [JOI 2021 Final] 地牢 3 题解

P7408 [JOI 2021 Final] 地牢 3 题解

时间:2024-10-30 17:20:00浏览次数:1  
标签:std pre le P7408 int 题解 kMaxN 2021 vec

Description

有一个 \(N+1\) 层的地牢,在地牢里有 \(M\) 个玩家。地牢的每层从入口开始,用 \(1\) 到 \(N+1\) 的整数编号。玩家从 \(1\) 到 \(M\) 标号。

玩家使用能量从一层移动到下一层。玩家从第 \(i\ (1\le i\le N)\) 层移动到第 \(i+1\) 层所用的能量为 \(A_i\)。因为这是一个单向通行的地牢,所以玩家只能从第 \(i\) 层移动到第 \(i+1\) 层,并且要保证 \(1\le i\le N\)。

在第 \(1\) 到第 \(N\) 层的每层(包括第 \(1\) 和第 \(N\) 层)都有治疗泉。在第 \(i\) 层的治疗泉处,玩家可以花 \(B_i\) 金币,使自己的能量增加 \(1\)。只要玩家有足够的金币,他就可以多次使用治疗泉。但是,每个玩家都有最大能量,使用治疗泉的话也不能使自己的能量超过最大值。

现在玩家 \(j\ (1\le j\le M)\) 在第 \(S_j\) 层。他目前的能量为 \(0\)。他的最大能量是 \(U_j\)。他想要到第 \(T_j\) 层。在路上他的能量不能小于 \(0\)。他需要多少金币呢?

给定这个地牢和玩家的信息,写一个程序计算对于每个玩家,是否可以在移动过程中能量不小于 \(0\) 的情况下到达目的地。如果可以的话,计算他最少需要多少金币才能达成目标。

对于所有数据,满足:

\(1\le N,M\le 2\times 10^5\);
\(1\le A_i,B_i\le 2\times 10^5\ (1\le i\le N)\);
\(1\le S_j<T_j\le N+1\ (1\le j\le M)\);
\(1\le U_j\le 10^8\ (1\le j\le M)\)。

Solution

首先考虑对题意进行转化,设 \(c_i=\sum_{j<i}{a_j}\),将走地牢转化为在数轴上从 \(c_s\) 走到 \(c_t\),而使用治疗泉可以看作是在 \([c_i,c_i+U)\) 这个区间里用 \(b_i\) 的代价选择一个位置激活,问将 \([c_s,c_t)\) 里所有的位置都激活的最小代价。

容易发现一个位置 \(p\) 的代价是 \([p-U+1,p]\) 区间内关键点权值的最小值。

考虑对于每个关键点 \(i\) 算出其控制的区间,设 \(pre\) 为 \(i\) 之前第一个代价小于 \(b_i\) 的位置,\(nxt\) 为 \(i\) 之后第一个代价小于 \(b_i\) 的位置。

那么一个位置 \(p\) 被 \(i\) 控制当且仅当 \(p-U\geq c_{pre},p-U<c_i,p<c_{nxt}\),整理一下为:\(\max\{c_i,c_{pre}+U\}\leq p<\min\{c_i+U,c_{nxt}\}\)。

于是关键点 \(i\) 对答案的贡献为 \(b_i\times\left(\min\{c_i+U,c_{nxt}\}-\max\{c_i,c_{pre}+U\}\right)\),这是个关于 \(U\) 的分段一次函数,可以用动态开点线段树维护每个 \(U\) 的答案。

对于终点全是 \(n+1\) 的子任务直接将询问离线下来倒着枚举关键点,同时维护一个代价从栈顶到栈底递增的单调栈来求出每个点的 \(pre\) 和 \(nxt\)。

每次加入一个点 \(i\) 时,将所有 \(pre\) 为 \(i\) 的位置 \(j\) 之前的贡献去掉,再加上 \((j,pre_j,nxt_j)\) 的贡献,然后加入 \((i,0,nxt_i)\) 的贡献表示当 \(s=i\) 时 \(i\) 找不到 \(pre\) 时 \(i\) 对答案的贡献。

如果终点不固定,则找到询问为 \((s,n+1)\) 时控制 \(c_t\) 的关键点 \(k\),容易发现 \(c_t-U<c_k\leq c_t\),那么 \((s,n+1)\) 和 \((k,n+1)\) 两组询问在 \([c_t,c_{n+1})\) 的贡献是相等的,并且 \((k,n+1)\) 在 \([c_k,c_t)\) 的贡献为 \(b_k\cdot(c_t-c_k)\),所以 \(ans(s,t)=ans(s,n+1)-ans(k,n+1)+b_k\cdot(c_t-c_k)\),就转化为终点固定的情况了。

时间复杂度:\(O((n+m)\log V)\)。

Code

#include <bits/stdc++.h>

#define int int64_t

const int kMaxN = 2e5 + 5;

int n, m, rt, sgt_cnt;
int a[kMaxN], b[kMaxN], c[kMaxN], ans[kMaxN];
int lg[kMaxN], sta[kMaxN][20], stb[kMaxN][20];
int ls[kMaxN * 60], rs[kMaxN * 60], sum[kMaxN * 60], tag[kMaxN * 60];
std::vector<std::tuple<int, int, int>> qq[kMaxN];

int get(int x, int y) {
  return b[x] < b[y] ? x : y;
}

void st_prework() {
  lg[0] = -1;
  for (int i = 1; i <= n + 1; ++i) {
    sta[i][0] = a[i];
    stb[i][0] = i;
    lg[i] = lg[i >> 1] + 1;
  }
  for (int i = 1; i <= lg[n + 1]; ++i) {
    for (int j = 1; j <= n + 1 - (1 << i) + 1; ++j) {
      sta[j][i] = std::max(sta[j][i - 1], sta[j + (1 << (i - 1))][i - 1]);
      stb[j][i] = get(stb[j][i - 1], stb[j + (1 << (i - 1))][i - 1]);
    }
  }
}

int querya(int l, int r) {
  int k = lg[r - l + 1];
  return std::max(sta[l][k], sta[r - (1 << k) + 1][k]);
}

int queryb(int l, int r) {
  int k = lg[r - l + 1];
  return get(stb[l][k], stb[r - (1 << k) + 1][k]);
}

void prework() {
  st_prework();
}

void update(int &x, int l, int r, int ql, int qr, int v) {
  if (l > qr || r < ql) return;
  if (!x) x = ++sgt_cnt;
  sum[x] += 1ll * v * (std::min(r, qr) - std::max(l, ql) + 1);
  if (l >= ql && r <= qr) return void(tag[x] += v);
  int mid = (l + r) >> 1;
  update(ls[x], l, mid, ql, qr, v), update(rs[x], mid + 1, r, ql, qr, v);
}

int query(int x, int l, int r, int ql, int qr) {
  if (!x || l > qr || r < ql) return 0;
  else if (l >= ql && r <= qr) return sum[x];
  int mid = (l + r) >> 1;
  return query(ls[x], l, mid, ql, qr) + query(rs[x], mid + 1, r, ql, qr) + 1ll * tag[x] * (std::min(r, qr) - std::max(l, ql) + 1);
}

int res[kMaxN];

void upd(int l, int r, int k, int b, int v) {
  l = std::max<int>(l, 0), r = std::min<int>(r, 1e8);
  if (l > r || !k && !b) return;
  // std::cerr << l << ' ' << r << ' ' << k << ' ' << b << ' ' << v << '\n';
  update(rt, 0, 1e8, l, l, (k * l + b) * v);
  update(rt, 0, 1e8, l + 1, r, k * v);
  update(rt, 0, 1e8, r + 1, r + 1, -(k * r + b) * v);
  // for (int i = l; i <= r; ++i) res[i] += v * (k * i + b);
}

void work(int x, int pre, int nxt, int v) {
  assert(x > pre && x < nxt);
  std::vector<int> vec = {0, c[nxt] - c[x]};
  if (pre) vec.emplace_back(c[nxt] - c[pre]), vec.emplace_back(c[x] - c[pre]);
  else vec.emplace_back(1e8 + 1);
  std::sort(vec.begin(), vec.end());
  vec.erase(std::unique(vec.begin(), vec.end()), vec.end());
  for (int i = 0; i + 1 < (int)vec.size(); ++i) {
    int l = vec[i], r = vec[i + 1] - 1, k = 0, b = 0;
    if (c[x] + l <= c[nxt] && c[x] + r <= c[nxt]) ++k, b += c[x];
    else b += c[nxt];
    if (c[x] >= c[pre] + l && c[x] >= c[pre] + r) b -= c[x];
    else --k, b -= c[pre];
    upd(l, r, k, b, v * ::b[x]);
  }
  // for (int u = 0; u <= 1e3; ++u) {
  //   if (std::max(c[i], c[pre] + u) < std::min(c[i] + u, c[nxt]))
  //     res[u] += v * b[i] * (std::min(c[i] + u, c[nxt]) - std::max(c[i], c[pre] + u));
  // }
}

void getans() {
  static int top, stk[kMaxN], nxt[kMaxN];
  stk[top = 1] = n + 1;
  c[0] = -1e9;
  for (int i = n; i; --i) {
    for (; top && b[stk[top]] >= b[i]; --top) {
      work(stk[top], 0, nxt[stk[top]], -1);
      work(stk[top], i, nxt[stk[top]], 1);
    }
    nxt[i] = stk[top];
    stk[++top] = i;
    work(i, 0, nxt[i], 1);
    for (auto [id, u, v] : qq[i]) ans[id] += v * query(rt, 0, 1e8, 0, u);
    // for (auto [id, u, v] : qq[i]) ans[id] += v * res[u];
  }
}

void dickdreamer() {
  std::cin >> n >> m;
  for (int i = 1; i <= n; ++i) {
    std::cin >> a[i];
    c[i + 1] = c[i] + a[i];
  }
  for (int i = 1; i <= n; ++i)
    std::cin >> b[i];
  prework();
  for (int i = 1; i <= m; ++i) {
    int s, t, u;
    std::cin >> s >> t >> u;
    if (querya(s, t - 1) > u) {
      ans[i] = -1;
      continue;
    }
    int p = std::max(s, std::upper_bound(c + 1, c + 2 + n, c[t] - u) - c), k = queryb(p, t);
    ans[i] = b[k] * (c[t] - c[k]);
    qq[s].emplace_back(i, u, 1), qq[k].emplace_back(i, u, -1);
  }
  getans();
  for (int i = 1; i <= m; ++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;
}

标签:std,pre,le,P7408,int,题解,kMaxN,2021,vec
From: https://www.cnblogs.com/Scarab/p/18516231

相关文章

  • CF1187题解
    前言这套题相对来讲难度不算高,并且质量也很好,建议尝试CF1187A一眼秒,但我没有考虑s,t只有这一种排列方式,所以取一下\(max(n-s,n-t)\)#include<bits/stdc++.h>usingnamespacestd;intT,n,s,t;intmain(){ scanf("%d",&T); while(T--){ scanf("%d%d%d",&n,&s,&t)......
  • 动态规划题解报告
    Findacar注意到矩阵本质上是一个分形,即每次向右下复制当前矩阵,证明考虑归纳法。由此可以知道一个比较好的性质\(a_{i+k,j+k}=a_{i,j}\)其中\(k=2^n\),由于每次是复制加倍,所以横纵坐标都会加上一个\(2^n\),且每次增加的二次幂一定是横纵坐标二进制减一后没有的那一位(证明考虑......
  • CF2030 题解
    因为cf炸了所以没办法提供代码,抱歉喵。A给定序列,定义$mn_i=\min_{j\lei}a_j,mx_i=\max_{j\lei}a_j$。重排该序列,最大化$\sum_{i=1}^nmx_i-mn_i$。$n\le10^5$正解手玩出一个构造,把最大和最小值放在前两个位置,这样的价值是\((n-1)\times(mx-mn)\)。由于\(m......
  • 天眼销常见问题解答
    天眼销上线已经有一段时间了,用户在此期间提出了一些问题。经过我们的整理在这里为大家解答。回答问题整理1.你们的数据来源是哪?精确吗?本平台的企业数据来源于全网公开数据,包含全国企业信用信息公示系统,其中企业联系方式主要来源于全国企业信用信息公示系统中的公示的......
  • [CodeForces] CF628 题解
    A.TennisTournamentLink-CFLink-Luogu【题目大意】\(n\)个选手进行若干场比赛,胜者保留,败者淘汰。每场比赛为两人。每场比赛每个人需要\(b\)瓶水,裁判需要\(1\)瓶水。每个人参加这些比赛总共需要\(p\)条毛巾。注意:洛谷题面翻译有误!建议看英文版。【解题思路】每场比......
  • CSP-J2024 T1(poker/扑克)题解
    洛谷CSP-J2024自测指路前情提要:虽然洛谷讨论区里大多数都是倾向用哈希解决该题,但实际上可以用一些邪门小技巧来A这道题awa先来读题。题目中说小P想知道他至少得向小S借多少张牌,才能让从小S和小Q借来的牌中,可以选出52张牌构成一副完整的扑克牌。题目说了是求至少要......
  • 01背包问题(经典dp题解)
    有 N 件物品和一个容量是 V 的背包。每件物品只能使用一次。第 ii 件物品的体积是 vi,价值是 wi。求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大。输出最大价值。输入格式第一行两个整数,N,V,用空格隔开,分别表示物品数量和背包容积。接下来......
  • 2024湖南省赛题解(不全)
    湖南省赛K题题意你可以免费移动经过一条边,求在满足在任意点开始都能成功渡劫的最小花费。思路建一个虚拟源点,连向每一个点,将这条边的边权设为这个点渡劫需要的花费。跑最短路,这样会把每一种情况囊括在内,但是没有考虑免费的移动。建一个dist2数组,用来记录每一个点当前......
  • [一直更新中]一句话题解
    目录一句话题解2024.10.29AT_abc290_fAT_arc156_c2024.10.30P5749[IOI2019]排列鞋子AT_abc285_e一句话题解不能什么题都随便写写就过了,留点印象好一点。一直更新。2024.10.29AT_abc290_f组合数数。满足树的形态要有\(\sumdeg_i=2n-2\)。考虑目前有\(k\)个儿子节点,直径......
  • [错误代码] SQLSTATE[HY000] [1045] Access denied for user 'cs2021'@'localhost' (u
    错误分析错误代码:SQLSTATE[HY000][1045]Accessdeniedforuser'cs2021'@'localhost'(usingpassword:YES)错误类型:数据库连接错误错误原因:用户名或密码错误。数据库用户没有权限从 localhost 连接。MySQL服务未启动或配置问题。解决方案检查用户名和密码......