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

ethan_zhou
博客:blog-e.top搬运于
2025-08-24 22:16:10,当前版本为作者最后更新于2022-04-03 17:18:44,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
说一个 的 cdq 分治做法,常数不大,不用分类讨论,较为好写。
一些符号
- 记 表示下标在 到 之间的所有区间的左端点最大值, 意义类似。
- 记 $\operatorname{rng}(l,r)=[\operatorname{mxl}(l,r),\operatorname{mnr}(l,r)]$。
暴戾
首先是一个简单的 dp:
$$dp_i=1+\max_{[j<i]\land [(i-j)\in\operatorname{rng}(i+1,j)]}dp_j $$因为这个转移的条件比较复杂,合法的 都是不连续的,考虑用 cdq 分治来做。
cdq
假设我们目前已经知道 ,考虑这些 dp 值对 的贡献。
可以更新 当且仅当:
注意到条件 1 与 相关性较大,条件 2 与 相关性比较大,考虑分别满足两个条件。
我们可以开一个下标线段树 ,然后枚举 ,每次进行三类操作:
- 对于所有满足 的 ,。
- 对于所有满足 的 ,。
- $dp_j\gets\min_{j\in[j-\operatorname{mnr}(mid+1,r),j-\operatorname{mxl}(mid+1,r)]}$。
前 2 种操作保证只有满足 条件 1 的 此时才会在线段树里。 而第 3 个操作则从线段树中筛选出满足 条件 2 的 用来更新 。
具体实现就比较好搞了,可以先扫一下 ,然后其对应进行 1,2 操作的 处开
vector打个标记即可。#include <bits/stdc++.h> #define fi first #define se second using namespace std; using pi = pair<int, int>; const char nl = '\n'; const int MXN = 1e6 + 5, INF = 1e9, P = 1e9 + 7; int n; pi rng[MXN]; inline void upd(pi &x, const pi &y) { x.fi = max(x.fi, y.fi); x.se = min(x.se, y.se); } namespace segt { struct node { int mx, mxc; node(int mx = -INF, int mxc = 0) : mx(mx), mxc(mxc) {} } t[MXN << 2]; inline int norm(int x) { return x < P ? x : x - P; } inline node operator+(const node &x, const node &y) { if (x.mx == y.mx) return node(x.mx, norm(x.mxc + y.mxc)); if (x.mx > y.mx) return x; return y; } #define ls p << 1 #define rs p << 1 | 1 inline void pull(int p) { t[p] = t[ls] + t[rs]; } void _mdf(int p, int l, int r, int mi, const node &mv) { if (l == r) { t[p] = mv; return; } int mid = (l + r) >> 1; if (mi <= mid) _mdf(ls, l, mid, mi, mv); else _mdf(rs, mid + 1, r, mi, mv); pull(p); } node _qry(int p, int l, int r, int ql, int qr) { if (qr < l || r < ql) return node(); if (ql <= l && r <= qr) return t[p]; int mid = (l + r) >> 1; return _qry(ls, l, mid, ql, qr) + _qry(rs, mid + 1, r, ql, qr); } int sz; void init(int _sz) { sz = _sz; fill(t + 1, t + 1 + (sz << 2), node()); } void mdf(int mi, const node &mv) { _mdf(1, 1, sz, mi, mv); } node qry(int ql, int qr) { return _qry(1, 1, sz, ql, qr); } } // namespace segt using segt::init; using segt::mdf; using segt::qry; vector<pi> id[MXN]; segt::node dp[MXN]; void solve(int l, int r) { if (r - l <= 50) { for (int i = l; i <= r; i++) { pi tmp = {1, n}; for (int j = i - 1; j >= l; j--) { upd(tmp, rng[j + 1]); if (i - j > tmp.se) break; if (i - j >= tmp.fi) dp[i] = dp[i] + segt::node(dp[j].mx + 1, dp[j].mxc); } } return; } int mid = (l + r) >> 1; solve(l, mid); pi tmp = {1, n}; for (int i = mid + 1; i <= r; i++) id[i].clear(); for (int i = mid; i >= l; i--) { if (tmp.se < tmp.fi || i + tmp.se <= mid) break; if (i + tmp.fi <= r) { id[max(mid + 1, i + tmp.fi)].push_back({i, 1}); id[min(r + 1, i + tmp.se + 1)].push_back({i, 0}); } upd(tmp, rng[i]); } init(mid - l + 1); tmp = {1, n}; for (int i = mid + 1; i <= r; i++) { for (auto j : id[i]) { if (j.se) mdf(j.fi - l + 1, dp[j.fi]); else mdf(j.fi - l + 1, segt::node()); } upd(tmp, rng[i]); if (tmp.se < tmp.fi || i - tmp.se > mid) break; segt::node res = qry(max(l, i - tmp.se) - l + 1, min(mid, i - tmp.fi) - l + 1); ++res.mx; dp[i] = dp[i] + res; } solve(mid + 1, r); } int main() { ios::sync_with_stdio(0); cin.tie(0); cin >> n; pi tmp = {1, n}; for (int i = 1; i <= n; i++) { cin >> rng[i].fi >> rng[i].se; upd(tmp, rng[i]); } dp[0] = {0, 1}; solve(0, n); if (dp[n].mx < 0) cout << "NIE"; else cout << dp[n].mx << " " << dp[n].mxc; return 0; }
- 1
信息
- ID
- 4986
- 时间
- 7000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者