Solution Set【2024.1.5】
[POI2011] Lightning Conductor / [JSOI2016] 灯塔
设 \(f_i\) 表示只考虑 \(\left[1, i\right]\) 的情况下 \(i\) 的答案,那么有:
发现其满足四边形不等式,于是使用分治优化即可。
时间复杂度 \(O(n\log n)\)。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef long double realType;
typedef std::vector<realType> RealVector;
typedef std::vector<valueType> ValueVector;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::deque<ValueTuple> TupleDeque;
constexpr realType MAX = std::numeric_limits<realType>::max();
void solve(valueType AnsL, valueType AnsR, valueType L, valueType R, RealVector &F, std::function<realType(valueType, valueType)> &W) {
if (AnsL > AnsR)
return;
valueType const mid = (AnsL + AnsR) >> 1;
F[mid] = MAX;
valueType point = 0;
for (valueType i = L; i <= std::min(mid - 1, R); ++i) {
realType const value = W(i, mid);
if (value < F[mid]) {
F[mid] = value;
point = i;
}
}
solve(AnsL, mid - 1, L, point, F, W);
solve(mid + 1, AnsR, point, R, F, W);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
RealVector A(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
RealVector F(N + 1, MAX), G(N + 1, MAX);
std::function<realType(valueType, valueType)> W = [&](valueType l, valueType r) {// weight of [l, r]
if (l > r)
return MAX;
return (realType) A[r] - (realType) A[l] - (realType) std::sqrt(static_cast<realType>((realType) r - (realType) l));
};
solve(1, N, 1, N, F, W);
std::reverse(A.begin() + 1, A.end());
solve(1, N, 1, N, G, W);
std::reverse(G.begin() + 1, G.end());
for (valueType i = 1; i <= N; ++i)
std::cout << (valueType) std::ceil(std::max<realType>(-std::min(F[i], G[i]), 0)) << '\n';
std::cout << std::flush;
return 0;
}
CF833B The Bakery / CF1527E Partition Game
数对贡献分析类似于 CF868F Yet Another Minimization Problem,略去。
Code of CF833B
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
class Recorder {
private:
valueType N, ans;
valueType l, r;
ValueVector A, bucket;
void Insert(valueType x) {
if (bucket[x]++ == 0)
++ans;
}
void Remove(valueType x) {
if (--bucket[x] == 0)
--ans;
}
void Move(valueType L, valueType R) {
while (l > L)
Insert(A[--l]);
while (r < R)
Insert(A[++r]);
while (l < L)
Remove(A[l++]);
while (r > R)
Remove(A[r--]);
}
public:
Recorder() = default;
Recorder(valueType n, ValueVector A) : N(n), ans(0), l(1), r(0), A(std::move(A)), bucket(N + 1, 0) {}
void assign(valueType n, ValueVector A) {
N = n;
ans = 0;
l = 1;
r = 0;
this->A = std::move(A);
bucket.assign(N + 1, 0);
}
valueType GetAns(valueType l, valueType r) {
Move(l, r);
return ans;
}
valueType GetAns() const {
return ans;
}
valueType operator()(valueType l, valueType r) {
return GetAns(l, r);
}
valueType operator()() const {
return GetAns();
}
};
constexpr valueType MAX = std::numeric_limits<valueType>::max() >> 1, MIN = std::numeric_limits<valueType>::min() >> 1;
void solve(valueType AnsL, valueType AnsR, valueType L, valueType R, ValueVector &F, ValueVector const &last, Recorder &W) {
if (AnsL > AnsR)
return;
valueType const mid = (AnsL + AnsR) >> 1;
F[mid] = MIN;
valueType point = 0;
for (valueType i = L; i <= std::min(mid - 1, R); ++i) {
valueType const value = last[i] + W(i + 1, mid);
if (value > F[mid]) {
F[mid] = value;
point = i;
}
}
solve(AnsL, mid - 1, L, point, F, last, W);
solve(mid + 1, AnsR, point, R, F, last, W);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, K;
std::cin >> N >> K;
ValueVector A(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
Recorder W(N, A);
ValueMatrix F(K + 1, ValueVector(N + 1, 0));
for (valueType i = 1; i <= N; ++i)
F[1][i] = W(1, i);
for (valueType i = 2; i <= K; ++i)
solve(i, N, 1, N, F[i], F[i - 1], W);
std::cout << F[K][N] << std::endl;
return 0;
}
Code of CF1527E
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::deque<valueType> ValueQueue;
typedef std::vector<ValueQueue> QueueVector;
class Recorder {
private:
valueType N, ans;
valueType l, r;
ValueVector A;
QueueVector bucket;
valueType calc(valueType x) {
if (bucket[x].size() < 2)
return 0;
return bucket[x].back() - bucket[x].front();
}
void InsertLeft(valueType x) {
ans -= calc(A[x]);
bucket[A[x]].push_front(x);
ans += calc(A[x]);
}
void RemoveLeft(valueType x) {
ans -= calc(A[x]);
bucket[A[x]].pop_front();
ans += calc(A[x]);
}
void InsertRight(valueType x) {
ans -= calc(A[x]);
bucket[A[x]].push_back(x);
ans += calc(A[x]);
}
void RemoveRight(valueType x) {
ans -= calc(A[x]);
bucket[A[x]].pop_back();
ans += calc(A[x]);
}
void Move(valueType L, valueType R) {
while (l > L)
InsertLeft(--l);
while (r < R)
InsertRight(++r);
while (l < L)
RemoveLeft(l++);
while (r > R)
RemoveRight(r--);
}
public:
Recorder() = default;
Recorder(valueType n, ValueVector A) : N(n), ans(0), l(1), r(0), A(std::move(A)), bucket(N + 1) {}
void assign(valueType n, ValueVector A) {
N = n;
ans = 0;
l = 1;
r = 0;
this->A = std::move(A);
bucket.resize(N + 1);
}
valueType GetAns(valueType l, valueType r) {
Move(l, r);
return ans;
}
valueType GetAns() const {
return ans;
}
valueType operator()(valueType l, valueType r) {
return GetAns(l, r);
}
valueType operator()() const {
return GetAns();
}
};
constexpr valueType MAX = std::numeric_limits<valueType>::max() >> 1;
void solve(valueType AnsL, valueType AnsR, valueType L, valueType R, ValueVector &F, ValueVector const &last, Recorder &W) {
if (AnsL > AnsR)
return;
valueType const mid = (AnsL + AnsR) >> 1;
F[mid] = MAX;
valueType point = 0;
for (valueType i = L; i <= std::min(mid - 1, R); ++i) {
valueType const value = last[i] + W(i + 1, mid);
if (value < F[mid]) {
F[mid] = value;
point = i;
}
}
solve(AnsL, mid - 1, L, point, F, last, W);
solve(mid + 1, AnsR, point, R, F, last, W);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, K;
std::cin >> N >> K;
ValueVector A(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
Recorder W(N, A);
ValueMatrix F(K + 1, ValueVector(N + 1, 0));
for (valueType i = 1; i <= N; ++i)
F[1][i] = W(1, i);
for (valueType i = 2; i <= K; ++i)
solve(i, N, 1, N, F[i], F[i - 1], W);
std::cout << F[K][N] << std::endl;
return 0;
}
[CEOI2004] 锯木厂选址
设 \(f_{k, i}\) 表示在点 \(i\) 设置第 \(k\) 个锯木厂的最小代价,那么有:
其中 \(w\left(i, j\right)\) 表示区间 \(\left[i, j\right]\) 的木材运送至点 \(j\) 的代价。下面证明其满足四边形不等式。
首先,\(w\left(l, r\right)\) 满足四边形不等式,当且仅当:
移项可得:
即 \(f_l\left(r\right) = w\left(l, r\right) - w\left(l + 1, r\right)\) 随 \(r\) 的增长单调不减,不难发现 \(f_l\left(r\right)\) 可以表示为将点 \(l\) 的木材运送至点 \(r\) 的代价,由于距离和代价均为非负整数,因此 \(f_l\left(r\right)\) 单调不减,即 \(w\left(l, r\right)\) 满足四边形不等式。
进而可以推知 \(f_{k, i}\) 满足决策单调性,因此使用分治优化转移即可。
复杂度为 \(O\left(nk\log n\right)\),其中 \(k = 3\)。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
constexpr valueType MAX = std::numeric_limits<valueType>::max() >> 1, MIN = std::numeric_limits<valueType>::min() >> 1;
void solve(valueType AnsL, valueType AnsR, valueType L, valueType R, ValueVector &F, ValueVector const &last, std::function<valueType(valueType, valueType)> const &W) {
if (AnsL > AnsR)
return;
valueType const mid = (AnsL + AnsR) >> 1;
F[mid] = MAX;
valueType point = 0;
for (valueType i = L; i <= std::min(mid - 1, R); ++i) {
valueType const value = last[i] + W(i + 1, mid);
if (value < F[mid]) {
F[mid] = value;
point = i;
}
}
solve(AnsL, mid - 1, L, point, F, last, W);
solve(mid + 1, AnsR, point, R, F, last, W);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
valueType const K = std::min<valueType>(3, N + 1);
ValueVector dist(N + 2, 0), weight(N + 2, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> weight[i] >> dist[i];
++N;
std::partial_sum(dist.rbegin(), dist.rend(), dist.rbegin());
ValueVector A(N + 1, 0), C(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
A[i] = weight[i] * dist[i];
std::partial_sum(weight.begin(), weight.end(), C.begin());
std::partial_sum(A.begin(), A.end(), A.begin());
std::function<valueType(valueType, valueType)> W = [&](valueType l, valueType r) {
valueType const count = C[r] - C[l - 1];
return A[r] - A[l - 1] - count * dist[r];
};
ValueMatrix F(K + 1, ValueVector(N + 1, 0));
for (valueType i = 1; i <= N; ++i)
F[1][i] = W(1, i);
for (valueType i = 2; i <= K; ++i)
solve(i, N, 1, N, F[i], F[i - 1], W);
std::cout << F[K][N] << std::endl;
return 0;
}
CF1603D Artistic Partition
首先考虑化简代价函数 \(c\left(l, r\right)\):
其中 \(p\left(n\right) = \sum\limits_{i \le n} \varphi(i)\),即欧拉函数前缀和。
注意到若 \(\left\lfloor\frac{r}{l}\right\rfloor = 1\),那么有 \(c\left(l, r\right) = \sum\limits_{d = l}^{r}p(1) = r - l + 1\)。不难发现 \(r - l + 1\) 为 \(c\left(l, r\right)\) 的最小值。同时我们有 \(r < 2 \cdot l \Rightarrow c\left(l, r\right) = r - l + 1\)。因此若我们可以将 \(\left[1, n\right]\) 划分为若干满足 \(r < 2 \cdot l\) 的区间,那么答案即为 \(n\)。
可以发现一种划分方式:\(\left[1, 1\right], \left[2, 3\right], \left[4, 7\right], \left[8, 15\right], \cdots \left[2^{L - 1}, 2^L - 1\right], \left[2^L, n\right]\) 共 \(\left\lceil \log_2 n \right\rceil\) 个区间。因此我们有当 \(k \ge \left\lceil \log_2 n \right\rceil\) 时,答案为 \(n\)。
这样我们便只需要处理 \(k \le \log_2 n\) 的情况。发现瓶颈在于求 \(c\left(l, r\right)\)。根据整除分块的结论,我们可以发现对于每个 \(r\),\(\left\lfloor\frac{r}{d}\right\rfloor\) 的值只有 \(\sqrt{r}\) 种,因此我们可以对每种不同的 \(\left\lfloor\frac{r}{d}\right\rfloor\) 值进行后缀和预处理,以便可以在 \(\mathcal{O}(1)\) 的时间内求出 \(c\left(l, r\right)\)。这部分预处理的时空复杂度为 \(\mathcal{O}(n\sqrt{n})\)。
接下来发现转移仍需优化,发现代价函数 \(c\left(l, r\right)\) 满足四边形不等式,下面给出证明:
首先,\(c\left(l, r\right)\) 满足四边形不等式,当且仅当:
移项可得:
即 \(f_l\left(r\right) = c\left(l, r\right) - c\left(l + 1, r\right)\) 随 \(r\) 的增长单调不减,不难发现 \(f_l\left(r\right) = p(\left\lfloor\frac{r}{l}\right\rfloor)\),由于 \(\left\lfloor\frac{r}{l}\right\rfloor\) 和 \(p(x)\) 均单调不减,因此 \(f_l\left(r\right)\) 单调不减,即 \(c\left(l, r\right)\) 满足四边形不等式。
进而可以推知此时的转移满足决策单调性,因此使用分治优化转移即可。
时间复杂度为 \(\mathcal{O}(n\log^2_2 n + n \sqrt{n})\),空间复杂度为 \(\mathcal{O}(n \log_2 n + n\sqrt{n})\)。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
constexpr valueType MAX = std::numeric_limits<valueType>::max() >> 1;
void solve(valueType AnsL, valueType AnsR, valueType L, valueType R, ValueVector &F, ValueVector const &last, std::function<valueType(valueType, valueType)> &W) {
if (AnsL > AnsR)
return;
valueType const mid = (AnsL + AnsR) >> 1;
F[mid] = MAX;
valueType point = 0;
for (valueType i = L; i <= std::min(mid - 1, R); ++i) {
valueType const value = last[i] + W(i + 1, mid);
if (value < F[mid]) {
F[mid] = value;
point = i;
}
}
solve(AnsL, mid - 1, L, point, F, last, W);
solve(mid + 1, AnsR, point, R, F, last, W);
}
constexpr valueType MAXN = 1e5 + 5, MAXK = 20;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
ValueVector Phi(MAXN + 1, 0);
std::iota(Phi.begin(), Phi.end(), 0);
for (valueType i = 2; i <= MAXN; ++i) {
if (Phi[i] == i)
for (valueType j = i; j <= MAXN; j += i)
Phi[j] -= Phi[j] / i;
}
std::partial_sum(Phi.begin(), Phi.end(), Phi.begin());
ValueMatrix Less(MAXN + 1), Greater(MAXN + 1);
ValueVector Limit(MAXN + 1, 0);
for (valueType i = 1; i <= MAXN; ++i) {
Limit[i] = std::sqrt(i);
assert(Limit[i] * Limit[i] <= i && (Limit[i] + 1) * (Limit[i] + 1) > i);
Less[i].resize(Limit[i] + 1);
for (valueType j = 1; j <= Limit[i]; ++j) {
Less[i][j] = Less[i][j - 1];
Less[i][j] += Phi[j] * (i / j - i / (j + 1));
}
Greater[i].resize(i / (Limit[i] + 1) + 2);
Greater[i][i / (Limit[i] + 1) + 1] = Less[i][Limit[i]];
for (valueType j = i / (Limit[i] + 1); j >= 1; --j) {
Greater[i][j] = Greater[i][j + 1];
Greater[i][j] += Phi[i / j];
}
}
std::function<valueType(valueType, valueType)> W = [&](valueType l, valueType r) {
if (l > r)
return MAX;
if (r / l <= Limit[r]) {
return Less[r][r / l] - Phi[r / l] * (l - 1 - r / (r / l + 1));
} else {
return Greater[r][l];
}
};
ValueMatrix F(MAXK + 1, ValueVector(MAXN + 1, 0));
for (valueType i = 1; i <= MAXN; ++i)
F[1][i] = W(1, i);
for (valueType i = 2; i <= MAXK; ++i)
solve(i, MAXN, 1, MAXN, F[i], F[i - 1], W);
valueType T;
std::cin >> T;
for (valueType testcase = 0; testcase < T; ++testcase) {
valueType N, K;
std::cin >> N >> K;
if (K > MAXK || (1ll << K) > N)
std::cout << N << '\n';
else
std::cout << F[K][N] << '\n';
}
std::cout << std::flush;
return 0;
}
「雅礼集训 2017 Day5」珠宝 /「NAIPC2016」Jewel Thief
发现物品的代价范围很小,故考虑对每种代价的的物品分组考虑。
发现对于代价相同的物品,其选取策略为先选取价值最大的物品,因此对价值进行降序排序后前缀和即可得到对于这类代价的物品的价值。不妨设 \(g_{v, i}\) 表示只考虑代价为 \(v\) 的物品的情况下,前 \(i\) 个物品的最大价值。设 \(f_{v, i}\) 表示考虑代价不大于 \(v\) 的物品的情况下,使用 \(i\) 个代价可以获得的最大价值,那么有:
不难发现在第 \(v\) 层的转移中,可以相互转移的状态在模 \(v\) 下是同余的,将其提取出来,可以得到:
不难发现 \(g_{v, i - j}\) 即为代价函数,由于其本质上为某个单调不升序列的前缀和,因此其满足四边形恒等式,使用分治优化转移即可。
复杂度为 \(\mathcal{O}(n \log n + ks\log k)\),其中 \(s\) 为代价的最大值。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
constexpr valueType MIN = std::numeric_limits<valueType>::min() >> 3;
constexpr valueType MAXS = 300;
void solve(valueType AnsL, valueType AnsR, valueType L, valueType R, ValueVector &F, ValueVector const &Last, ValueVector const &Weight, valueType Mod, valueType Remain) {
if (AnsL > AnsR)
return;
valueType const mid = (AnsL + AnsR) >> 1;
F[mid * Mod + Remain] = MIN;
valueType pos = L;
for (valueType i = L; i <= std::min(mid, R); ++i) {
valueType const value = Last[i * Mod + Remain] + Weight[mid - i];
if (value > F[mid * Mod + Remain]) {
F[mid * Mod + Remain] = value;
pos = i;
}
}
solve(AnsL, mid - 1, L, pos, F, Last, Weight, Mod, Remain);
solve(mid + 1, AnsR, pos, R, F, Last, Weight, Mod, Remain);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, K;
std::cin >> N >> K;
ValueMatrix A(MAXS + 1);
for (valueType i = 0; i < N; ++i) {
valueType c, v;
std::cin >> c >> v;
A[c].push_back(v);
}
ValueMatrix F(2, ValueVector(K + 1, MIN));
F[0][0] = 0;
for (valueType s = 1; s <= MAXS && s <= K; ++s) {
valueType const now = s & 1, prev = now ^ 1;
valueType const L = K / s;
std::sort(A[s].begin(), A[s].end(), std::greater<>());
std::partial_sum(A[s].begin(), A[s].end(), A[s].begin());
F[now].assign(K + 1, MIN);
A[s].insert(A[s].begin(), 0);
A[s].resize(L + 1, MIN);
for (valueType r = 0; r < s; ++r)
solve(0, (K - r) / s, 0, (K - r) / s, F[now], F[prev], A[s], s, r);
}
for (valueType i = 1; i <= K; ++i)
F[std::min(MAXS, K) & 1][i] = std::max(F[std::min(MAXS, K) & 1][i], F[std::min(MAXS, K) & 1][i - 1]);
for (valueType i = 1; i <= K; ++i)
std::cout << F[std::min(MAXS, K) & 1][i] << ' ';
std::cout << std::endl;
return 0;
}