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

raincity
**搬运于
2025-08-24 23:12:18,当前版本为作者最后更新于2025-06-13 12:07:33,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题意
数轴上的一些整点上放了牛和草。你每秒可以选择一头牛,令其向左或向右移动一个单位长度。每当一头牛和一捆草位于同一个位置时,牛就会吃掉这捆草。求吃完所有草的最短时间。
牛和草的初始位置以特殊方式给出:给定一个常量 。对于牛,给出 个区间 ,表示 这些位置放了牛,保证 。对于草同理。
分析
先考虑 Subtask 1,即牛和草的总数不超过 时怎么做。
考虑分析一头牛的行为。一头牛可以吃光它经过的所有位置上放的草,而它经过的所有位置是一段区间。并且,牛经过了整个区间,当且仅当它经过了区间的左右端点,因此固定区间时一头牛的代价很容易计算出来。
考虑分析所有牛的行为。通过调整法可以发现,可以认为任意两头牛的区间都是不交的。
考虑一个很暴力的转化:把数轴按照牛和草初始所在的位置分段,则最优解中每一段中所有边经过的次数相等,且均为 。因此直接枚举的状态量非常少。
考虑判定一个 的方案是否合法。考虑找充要条件。列几个条件可以发现,合法当且仅当:
- 草两侧两条线段不同时为 ,不分别为 和 。
- 牛两侧两条线段不同时为 ,不同时为 。
- 每个非 的段至少经过一头牛。
用这个充要条件容易写出 DP。设 ()表示:考虑了与前 个牛和草相邻的边,
- :最后一段未经过牛,最后一条边权值为 。
- :最后一段未经过牛,最后一条边权值为 。
- :最后一段已经过牛,最后一条边权值为 。
- :最后一段已经过牛,最后一条边权值为 。
- :最后一条边权值为 。
转移是容易的。注意特判同一个位置有两头牛的情况,此时两头牛可以分别移动。
然后一般情况是容易的。观察到 为定值,考虑每次跳过一个周期。本质不同的周期个数是 的。转移可以用 矩阵乘法描述。线段树 + 快速幂即可,复杂度 ,带 的常数。
实现
#include <bits/stdc++.h> using namespace std; #define rep(i, l, r) for (int i = (l); i <= (r); ++i) #define all(x) begin(x), end(x) #define sz(x) int((x).size()) using ll = long long; using pi = pair<int, int>; using vi = vector<int>; struct Mat { ll a[5][5]; Mat operator*(const Mat &b) const { Mat ans; memset(ans.a, 0x3f, sizeof ans.a); rep (i, 0, 4) rep (j, 0, 4) rep (k, 0, 4) ans.a[i][k] = min(ans.a[i][k], a[i][j] + b.a[j][k]); return ans; } static Mat Make(int b, ll d) { Mat ans; memset(ans.a, 0x3f, sizeof ans.a); if (b == 0) { fill(all(ans.a[2]), d); fill(all(ans.a[3]), 2 * d); memset(ans.a[4], 0, sizeof ans.a[4]); } else if (b == 1) { ans.a[2][1] = ans.a[2][2] = ans.a[2][4] = d; ans.a[3][0] = ans.a[3][3] = ans.a[3][4] = 2 * d; memset(ans.a[4], 0, sizeof ans.a[4]); } else { ans.a[0][0] = ans.a[0][4] = d; ans.a[1][1] = ans.a[1][4] = 2 * d; ans.a[2][2] = d; ans.a[3][3] = 2 * d; ans.a[4][2] = ans.a[4][3] = 0; } return ans; } }; struct I { ll c, g; ll bg, ed; int b; Mat mul; I operator+(const I &x) const { if (c == 0 && g == 0 && x.c == 0 && x.g == 0) return {.ed = ed + x.ed}; if (c == 0 && g == 0) { I ans = x; ans.bg += ed; return ans; } if (x.c == 0 && x.g == 0) { I ans = *this; ans.ed += x.ed; return ans; } return {c + x.c, g + x.g, bg, x.ed, x.b, x.mul * Mat::Make(b, ed + x.bg) * mul}; } I operator*(ll n) const { assert(n >= 0); I ans = {}, add = *this; for (; n > 0; n >>= 1) { if ((n & 1) == 1) ans = ans + add; add = add + add; } return ans; } }; vector<ll> raw; struct SegT { static constexpr int N = (1 << 16) + 10; int n; I t[2 * N]; void Build() { n = sz(raw) == 2 ? 1 : 2 << (31 ^ __builtin_clz(sz(raw) - 2)); rep (i, 0, n - 1) { t[i + n] = {.ed = i <= sz(raw) - 2 ? raw[i + 1] - raw[i] : 0}; memset(t[i + n].mul.a, 0x3f, sizeof t[i + n].mul.a); rep (j, 0, 4) t[i + n].mul.a[j][j] = 0; } for (int i = n - 1; i >= 1; --i) t[i] = t[2 * i] + t[2 * i + 1]; } void Upd(int x, ll c, ll g) { x += n; t[x].c += c, t[x].g += g; if (t[x].c > 1) { t[x].b = 0; } else if (t[x].c > 0) { t[x].b = 1; } else if (t[x].g > 0) { t[x].b = 2; } while (x > 1) { x >>= 1; t[x] = t[2 * x] + t[2 * x + 1]; } } I Qry() { return t[1]; } } sgt; ll m; int n, p; struct E { ll t, c, g; bool operator<(E x) const { return t < x.t; } }; vector<E> e; int main() { cin.tie(nullptr)->sync_with_stdio(false); cin.exceptions(cin.failbit); cin >> m >> n >> p; raw = {0, m}; rep (i, 1, n) { ll l, r; cin >> l >> r; raw.push_back(l % m); e.push_back({l, 1, 0}), e.push_back({r + m, -1, 0}); } rep (i, 1, p) { ll l, r; cin >> l >> r; raw.push_back(l % m); e.push_back({l, 0, 1}), e.push_back({r + m, 0, -1}); } sort(all(raw)); raw.erase(unique(all(raw)), end(raw)); sort(all(e)); sgt.Build(); I ans = {}; ll lst = -1; for (auto [t, c, g] : e) { if (lst != -1) ans = ans + sgt.Qry() * (t / m - lst); lst = t / m; sgt.Upd(lower_bound(all(raw), t % m) - begin(raw), c, g); } ll f[5]; rep (i, 0, 4) f[i] = ans.mul.a[i][4]; if (ans.b <= 1) cout << *min_element(all(f)) << '\n'; else cout << min(f[2], f[3]) << '\n'; }
- 1
信息
- ID
- 11915
- 时间
- 4000ms
- 内存
- 256MiB
- 难度
- 7
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者