1 条题解
-
0
自动搬运
来自洛谷,原作者为

CmsMartin
**搬运于
2025-08-24 22:45:21,当前版本为作者最后更新于2024-11-27 18:42:46,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
有神秘的分治做法,有点厉害。写个无脑做法。
定义 表示钦定 考虑前 个且钦定第 个不爆炸的方案数。
转移只有一种情况,假设 中的都无法引爆 ,就令 。这样以后我们只需要求出一个炸弹 可以引爆的最右边的炸弹 和最右侧可以引爆 的炸弹 。
这个求法只用维护一个单调栈然后二分就可以了。于是就有 ,这样子使用树状数组就可以了。
时间复杂度 。
int n, st[N], top, L[N], R[N]; ll a[N], r[N]; vector<int> buc[N]; mint c[N], f[N]; int lb(int x) { return x & -x; } void add(int x, mint v) { for (; x < N; x += lb(x)) c[x] += v; } mint ask(int x) { mint res(0); if(x <= 0) return res; for (; x; x -= lb(x)) res += c[x]; return res; } int main() { ios::sync_with_stdio(false); cin.tie(0); cout.tie(0); cin >> n; for (int i = 1; i <= n; i++) cin >> a[i] >> r[i]; a[0] = -inf, a[n + 1] = inf; f[0] = 1; add(1, 1); r[0] = r[n + 1] = inf * 2; st[top = 1] = 0; R[0] = n + 1; for (int i = 1; i <= n; i++) { auto get = [=](int u) -> ll { return a[u] + r[u]; }; int l = 1, r = top, res = 1; while (l <= r) { int mid = l + r >> 1; if (get(st[mid]) >= a[i]) res = mid, l = mid + 1; else r = mid - 1; } L[i] = st[res]; while (get(i) >= get(st[top])) top--; st[++top] = i; } st[top = 1] = n + 1; for (int i = n; i >= 1; i--) { auto get = [=](int u) -> ll { return a[u] - r[u]; }; int l = 1, r = top, res = 1; while (l <= r) { int mid = l + r >> 1; if (get(st[mid]) <= a[i]) res = mid, l = mid + 1; else r = mid - 1; } R[i] = st[res]; while (get(i) <= get(st[top])) top--; st[++top] = i; } auto query = [=](int l, int r) -> mint { return ask(r) - ask(l - 1); }; for (int i = 0; i <= n; i++) buc[R[i]].emplace_back(i); for (int i = 1; i <= n + 1; i++) { f[i] = query(L[i] + 1, i); for (int j : buc[i]) add(j + 1, -f[j]); add(i + 1, f[i]); } cout << f[n + 1].x << "\n"; return 0; }
- 1
信息
- ID
- 8417
- 时间
- 2000ms
- 内存
- 512MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者