[ZJOI2018] 胖 题解
前言
题目链接:洛谷。
题意简述
给定一个包含
对于每次询问求,使用 Bellman-Ford 算法计算过程中,每轮的
题目分析
解决一维问题的一个重要的 observation 是找到连续一段区间。这里我们不难发现,每条额外新增的边能够更新到的点是连续的一段区间。那么我们情不自禁地转换计数视角,要求每轮更新结点数量之和,就是每个点被更新次数之和,就是每条新增边更新的结点个数之和。问题转化为求出每一条新增边更新到点的区间,进一步,我们只需要快速求出区间的左右端点。左右本质相同,以下仅考虑求出区间左端点。
不难想到二分出这个左端点,考虑 check 一个
ST 表预处理
代码
#define NDEBUG #include <cstdio> #include <iostream> #include <cassert> #include <algorithm> using namespace std; const int N = 200010; int n, m; using lint = long long; const int lgN = __lg(N) + 1; struct ST { lint st[lgN][N]; inline void set(int p, lint v) { st[0][p] = v; } void init(int n) { for (int k = 1; k < lgN; ++k) for (int i = 1; i + (1 << k) - 1 <= n; ++i) st[k][i] = min(st[k - 1][i], st[k - 1][i + (1 << (k - 1))]); } lint query(int l, int r) { assert(l <= r); int p = __lg(r - l + 1); return min(st[p][l], st[p][r - (1 << p) + 1]); } } st[2]; lint pre[N]; int a[N], l[N]; int tmp[N], rev[N]; int main() { scanf("%d%d", &n, &m); for (int i = 1, w; i < n; ++i) { scanf("%d", &w); pre[i + 1] = pre[i] + w; } for (int k; m--; ) { scanf("%d", &k); for (int j = 1; j <= k; ++j) { scanf("%d%d", &a[j], &l[j]); tmp[j] = a[j], rev[a[j]] = l[j]; } sort(tmp + 1, tmp + k + 1); for (int i = 1; i <= k; ++i) { a[i] = tmp[i]; l[i] = rev[tmp[i]]; st[0].set(i, l[i] - pre[a[i]]); st[1].set(i, l[i] + pre[a[i]]); } st[0].init(k), st[1].init(k); auto queryL = [&] (int i) -> int { int x = a[i]; auto check = [=] (int mid) -> bool { lint dis = l[i] + pre[x] - pre[mid]; int dt = x - mid; int tl = lower_bound(tmp + 1, tmp + k + 1, max(1, mid - dt)) - tmp; int tr = upper_bound(tmp + 1, tmp + k + 1, mid) - tmp - 1; if (tl <= tr && st[0].query(tl, tr) + pre[mid] <= dis) return false; tl = tr + (!tr || tmp[tr] != mid), tr = i - 1; assert(tl == lower_bound(tmp + 1, tmp + k + 1, mid) - tmp); assert(tr == upper_bound(tmp + 1, tmp + k + 1, x - 1) - tmp - 1); if (tl <= tr && st[1].query(tl, tr) - pre[mid] <= dis) return false; return true; }; int L = 1, R = x - 1, ans = x, mid; while (L <= R) { mid = (L + R) >> 1; if (check(mid)) ans = mid, R = mid - 1; else L = mid + 1; } return ans; }; auto queryR = [&] (int i) -> int { int x = a[i]; auto check = [=] (int mid) -> bool { lint dis = l[i] + pre[mid] - pre[x]; int dt = mid - x; int tl = lower_bound(tmp + 1, tmp + k + 1, mid) - tmp; int tr = upper_bound(tmp + 1, tmp + k + 1, min(mid + dt - 1, n)) - tmp - 1; int ttt = tr; // don't include mid + dt if (tl <= tr && st[1].query(tl, tr) - pre[mid] <= dis) return false; tr = tl - (tl > k || tmp[tl] != mid), tl = i + 1; assert(tl == lower_bound(tmp + 1, tmp + k + 1, x + 1) - tmp); assert(tr == upper_bound(tmp + 1, tmp + k + 1, mid) - tmp - 1); if (tl <= tr && st[0].query(tl, tr) + pre[mid] <= dis) return false; if (mid + dt <= n) { if (ttt + 1 <= k && tmp[ttt + 1] <= mid + dt) ++ttt; assert(ttt == upper_bound(tmp + 1, tmp + k + 1, min(mid + dt, n)) - tmp - 1); if (tmp[ttt] == mid + dt) { if (st[1].st[0][ttt] - pre[mid] < dis) return false; } } return true; }; int L = x + 1, R = n, ans = x, mid; while (L <= R) { mid = (L + R) >> 1; if (check(mid)) ans = mid, L = mid + 1; else R = mid - 1; } return ans; }; lint ans = 0; for (int i = 1; i <= k; ++i) { int L = queryL(i), R = queryR(i); ans += R - L + 1; } printf("%lld\n", ans); } return 0; }
本文作者:XuYueming,转载请注明原文链接:https://www.cnblogs.com/XuYueming/p/18753770。
若未作特殊说明,本作品采用 知识共享署名-非商业性使用 4.0 国际许可协议 进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】