Solution Set【2024.1.2】
[SDOI2012] 任务安排 / 任务安排
设 \(f_i\) 表示前 \(i\) 个任务的最小花费,发现转移时需要前一部分分的批数,存在后效性。
考虑在每次分出新的一批任务时计算其对之后所有任务的贡献,有转移:
其中 \(st_i = \sum\limits_{j \le i} T_j, sc_i = \sum\limits_{j \le i} C_j\)。
上式可以化简为:
斜率优化即可。
注意到每次查询的斜率不存在单调性,在单调栈上二分即可。
时间复杂度 \(O(n \log n)\)。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef long double realType;
bool CompareLess(ValuePair const &A, ValuePair const &B, ValuePair const &C, ValuePair const &D) {
return (D.second - C.second) * (B.first - A.first) < (B.second - A.second) * (D.first - C.first);
}
bool CompareGreater(ValuePair const &A, ValuePair const &B, ValuePair const &C, ValuePair const &D) {
return (D.second - C.second) * (B.first - A.first) > (B.second - A.second) * (D.first - C.first);
}
template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, ValuePair const &C, ValuePair const &D, _Compare const &__compare) {
return __compare((D.second - C.second) * (B.first - A.first), (B.second - A.second) * (D.first - C.first));
}
bool CompareLess(ValuePair const &A, ValuePair const &B, ValuePair const &C) {
return CompareLess(A, B, B, C);
}
bool CompareGreater(ValuePair const &A, ValuePair const &B, ValuePair const &C) {
return CompareGreater(A, B, B, C);
}
template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, ValuePair const &C, _Compare const &__compare) {
return Compare(A, B, B, C, __compare);
}
bool CompareLess(ValuePair const &A, ValuePair const &B, valueType const K) {
return (B.second - A.second) < K * (B.first - A.first);
}
bool CompareGreater(ValuePair const &A, ValuePair const &B, valueType const K) {
return (B.second - A.second) > K * (B.first - A.first);
}
template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, valueType const K, _Compare const &__compare) {
return __compare(B.second - A.second, K * (B.first - A.first));
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, S;
std::cin >> N >> S;
ValueVector T(N + 1, 0), C(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> T[i] >> C[i];
std::partial_sum(T.begin(), T.end(), T.begin());
std::partial_sum(C.begin(), C.end(), C.begin());
ValueVector F(N + 1, 0);
PairVector Q;
Q.reserve(N + 1);
Q.emplace_back(C[0], F[0] - S * C[0]);
for (valueType i = 1; i <= N; ++i) {
valueType const K = T[i];
valueType pointer = (valueType) Q.size() - 1;
{
valueType left = 0, right = (valueType) Q.size() - 2;
while (left <= right) {
valueType const mid = (left + right) / 2;
if (Compare(Q[mid], Q[mid + 1], K, std::greater_equal<>())) {
right = mid - 1;
pointer = mid;
} else {
left = mid + 1;
}
}
}
auto const &[x, y] = Q[pointer];
F[i] = y - x * K + T[i] * C[i] + S * C[N];
ValuePair const Node(C[i], F[i] - S * C[i]);
while (Q.size() > 1 && Compare(Q[Q.size() - 2], Q[Q.size() - 1], Node, std::less_equal<>()))
Q.pop_back();
Q.push_back(Node);
}
std::cout << F[N] << std::endl;
return 0;
}
丝之割
可以发现,若数对 \(\left(u_0, v_0\right)\) 被除去,那么所有满足 \(u \ge u_0\) 且 \(v \le v_0\) 的数对均会被除去。
因此若将所有数对按 \(u\) 升序排列,那么剩余的有用的数对一定满足 \(v\) 严格递增。
这样之后每次消除数对一定是消除一个区间的数对,可以使用动态规划。
设 \(f_i\) 表示前 \(i\) 个数对的最小花费,那么有转移:
其中 \(u_i, v_i\) 表示第 \(i\) 个数对的两个数,维护出 \(a\) 的前缀最小值和 \(b\) 的后缀最小值即可。
这样转移式就可以化为斜率优化的形式,使用斜率优化即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef long double realType;
template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, ValuePair const &C, ValuePair const &D, _Compare const &__compare) {
return __compare((D.second - C.second) * (B.first - A.first), (B.second - A.second) * (D.first - C.first));
}
template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, ValuePair const &C, _Compare const &__compare) {
return Compare(A, B, B, C, __compare);
}
template<typename _Compare>
bool Compare(ValuePair const &A, ValuePair const &B, valueType const K, _Compare const &__compare) {
return __compare(B.second - A.second, K * (B.first - A.first));
}
constexpr valueType INF = std::numeric_limits<valueType>::max();
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, M;
std::cin >> N >> M;
ValueVector A(N + 10, INF), B(N + 10, INF);
PairVector C(M);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
for (valueType i = 1; i <= N; ++i)
std::cin >> B[i];
for (auto &[x, y] : C)
std::cin >> x >> y;
std::sort(C.begin(), C.end());
PairVector D;
D.reserve(M);
for (auto const &[x, y] : C) {
while (!D.empty() && D.back().first == x && D.back().second <= y)
D.pop_back();
if (D.empty() || D.back().second < y)
D.emplace_back(x, y);
}
M = (valueType) D.size() - 1;
ValueVector U(M + 10, N), V(M + 10, N);
for (valueType i = 0; i <= M; ++i)
std::tie(U[i], V[i]) = D[i];
for (valueType i = 1; i <= N; ++i)
A[i] = std::min(A[i], A[i - 1]);
for (valueType i = N; i >= 1; --i)
B[i] = std::min(B[i], B[i + 1]);
ValueVector F(M + 1, 0);
PairVector Q;
Q.reserve(M + 1);
Q.emplace_back(-A[U[0] - 1], 0);
valueType pointer = 0;
for (valueType i = 0; i <= M; ++i) {
valueType const K = B[V[i] + 1];
while (pointer + 1 < Q.size() && Compare(Q[pointer], Q[pointer + 1], K, std::less_equal<>()))
++pointer;
auto const &[x, y] = Q[pointer];
F[i] = y - x * K;
ValuePair const Node(-A[U[i + 1] - 1], F[i]);
while (Q.size() > 1 && Compare(Q[Q.size() - 2], Q[Q.size() - 1], Node, std::less_equal<>()))
Q.pop_back();
Q.push_back(Node);
}
std::cout << F[M] << std::endl;
return 0;
}
CF1599F Mars
题意可以转化为询问一个区间是否可以重排为一段模 \(P = 10^9 + 7\) 意义下的等差数列。
设等差数列的首项为 \(A\),区间长度为 \(n\),若我们得到区间内数的和 \(S\),那么根据等差数列求和公式:
我们可以得到 \(A\) 的表达式:
既然已经确定了公差,首项和长度,那么我们可以计算出该等差数列的每项的 \(k\) 次方和,以达到哈希的目的,再与原区间的 \(k\) 次方和比较即可。
考虑如何求出 \(k\) 次方和,可以发现其为:
可以使用二项式定理展开,得到:
\(\mathcal{O}(k)\) 计算即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<ValueMatrix> ValueCube;
namespace MODINT_WITH_FIXED_MOD {
constexpr valueType MOD = 1e9 + 7;
template<typename T1, typename T2, typename T3 = valueType>
void Inc(T1 &a, T2 b, const T3 &mod = MOD) {
a = a + b;
if (a >= mod)
a -= mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Dec(T1 &a, T2 b, const T3 &mod = MOD) {
a = a - b;
if (a < 0)
a += mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 sum(T1 a, T2 b, const T3 &mod = MOD) {
return a + b >= mod ? a + b - mod : a + b;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 sub(T1 a, T2 b, const T3 &mod = MOD) {
return a - b < 0 ? a - b + mod : a - b;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 mul(T1 a, T2 b, const T3 &mod = MOD) {
return (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
void Mul(T1 &a, T2 b, const T3 &mod = MOD) {
a = (long long) a * b % mod;
}
template<typename T1, typename T2, typename T3 = valueType>
T1 pow(T1 a, T2 b, const T3 &mod = MOD) {
T1 result = 1;
while (b > 0) {
if (b & 1)
Mul(result, a, mod);
Mul(a, a, mod);
b = b >> 1;
}
return result;
}
}// namespace MODINT_WITH_FIXED_MOD
using namespace MODINT_WITH_FIXED_MOD;
namespace HASH {
constexpr static valueType const HASH_COUNT = 8;
typedef std::array<valueType, HASH_COUNT> HashArray;
constexpr static HashArray const HASH_BASE = {1, 2, 3, 5, 7, 8, 9, 17};
struct Hash {
HashArray data;
Hash() : data() {
data.fill(0);
}
explicit Hash(valueType x) : data() {
for (valueType i = 0; i < HASH_COUNT; ++i)
data[i] = pow(x, HASH_BASE[i], MOD);
}
Hash(valueType x, HashArray last) : data() {
for (valueType i = 0; i < HASH_COUNT; ++i)
data[i] = sum(last[i], pow(x, HASH_BASE[i], MOD), MOD);
}
Hash(valueType x, Hash last) : data() {
for (valueType i = 0; i < HASH_COUNT; ++i)
data[i] = sum(last.data[i], pow(x, HASH_BASE[i], MOD), MOD);
}
bool operator==(Hash const &other) const {
return data == other.data;
}
bool operator!=(Hash const &other) const {
return data != other.data;
}
};
ValueMatrix HASH_POWER;
std::array<ValueMatrix, HASH_COUNT> HASH_COMBINE;
void initHashPower(valueType len) {
static constexpr valueType const HASH_MAX_POWER_COUNT = 20;
HASH_POWER.resize(HASH_MAX_POWER_COUNT, ValueVector(len + 1, 0));
for (valueType i = 0; i < HASH_MAX_POWER_COUNT; ++i) {
HASH_POWER[i][0] = i == 0;
for (valueType j = 1; j <= len; ++j)
HASH_POWER[i][j] = pow(j, i);
for (valueType j = 1; j <= len; ++j)
Inc(HASH_POWER[i][j], HASH_POWER[i][j - 1]);
}
/* ======================================================================= */
for (valueType i = 0; i < HASH_COUNT; ++i) {
valueType const k = HASH_BASE[i];
HASH_COMBINE[i].resize(k + 1, ValueVector(k + 1, 0));
HASH_COMBINE[i][0][0] = 1;
for (valueType j = 1; j <= k; ++j) {
HASH_COMBINE[i][j][0] = 1;
for (valueType l = 1; l <= j; ++l)
HASH_COMBINE[i][j][l] = sum(HASH_COMBINE[i][j - 1][l], HASH_COMBINE[i][j - 1][l - 1], MOD);
}
}
}
typedef std::vector<Hash> HashVector;
Hash GetHash(valueType n, valueType a, valueType d) {
Hash result;
for (valueType i = 0; i < HASH_COUNT; ++i) {
valueType const k = HASH_BASE[i];
for (valueType j = 0; j <= k; ++j)
Inc(result.data[i], mul(mul(HASH_COMBINE[i][k][j], HASH_POWER[j][n - 1]), mul(pow(a, k - j), pow(d, j))));
}
return result;
}
Hash GetSubHash(valueType l, valueType r, HashVector const &hash) {
Hash result;
for (valueType i = 0; i < HASH_COUNT; ++i)
result.data[i] = sub(hash[r].data[i], hash[l - 1].data[i]);
return result;
}
valueType GetSum(valueType l, valueType r, HashVector const &hash) {
return sub(hash[r].data[0], hash[l - 1].data[0]);
}
}// namespace HASH
using namespace HASH;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, Q;
std::cin >> N >> Q;
ValueVector A(N + 1);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
{
valueType const seed = std::chrono::steady_clock::now().time_since_epoch().count() ^ std::random_device()() ^ (unsigned long long) std::make_unique<char>().get();
valueType const shifting = std::mt19937(seed)() % MOD;
for (auto &x : A)
Inc(x, shifting);
}
initHashPower(N);
HashVector hash(N + 1);
for (valueType i = 1; i <= N; ++i)
hash[i] = Hash(A[i], hash[i - 1]);
for (valueType q = 0; q < Q; ++q) {
valueType l, r, D;
std::cin >> l >> r >> D;
valueType const S = GetSum(l, r, hash), L = r - l + 1;
valueType const A = sub(mul(S, pow(L, MOD - 2)), mul((MOD + 1) / 2, mul(L - 1, D)));
if (GetHash(L, A, D) == GetSubHash(l, r, hash))
std::cout << "Yes\n";
else
std::cout << "No\n";
}
std::cout << std::flush;
return 0;
}
CF713C Sonya and Problem Wihtout a Legend / 序列 sequence / CF13C Sequence / [USACO08FEB] Making the Grade G
若限制严格单调递增,可以将 \(a_i\) 均减去 \(i\),这样我们只需要使得 \(a_i\) 单调不降即可。
设 \(f_{i, j}\) 表示使得前 \(i\) 个数符合要求,且第 \(i\) 个数为 \(j\) 的最小花费,有转移:
设 \(g\) 为 \(f\) 的前缀最小值,那么有转移:
考虑设 \(F_i(x) = f_{i, x}, G_i(x) = g_{i, x}\),那么可以发现其均为关于 \(x\) 的凸函数,因此可以使用 Slope Trick 进行优化。
Code of CF713C
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::priority_queue<valueType, ValueVector, std::less<valueType>> ValueMaxHeap;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
valueType ans = 0;
ValueMaxHeap Q;
for (valueType i = 1; i <= N; ++i) {
valueType x;
std::cin >> x;
x -= i;
Q.push(x);
if (x < Q.top()) {
ans += Q.top() - x;
Q.pop();
Q.push(x);
}
}
std::cout << ans << std::endl;
return 0;
}
[ARC123D] Inc, Dec - Decomposition
由于 \(a_i\) 是给定的,因此若确定了 \(b_i\) 那么 \(c_i\) 也就确定了。
进而我们可以将题目限制转化为:
- \(b_i \le b_{i + 1}\)
- \(a_i - b_i \ge a_{i + 1} - b_{i + 1} \Leftrightarrow b_i + \left(a_{i + 1} - a_i\right) \le b_{i + 1}\)
设 \(s_i = \max\left\{0, a_{i + 1} - a_i\right\}\),那么上述条件即为 \(b_i + s_i \le b_{i + 1}\)
这样我们的问题就转化为了:
确定一个整数序列 \(b_i\),使得:
- \(b_i + s_i \le b_{i + 1}\)
- 最小化 \(\sum\limits_{i} \left\lvert b_i \right\rvert + \left\lvert a_i - b_i \right\rvert = \sum\limits_{i} \left\lvert b_i \right\rvert + \left\lvert b_i - a_i \right\rvert\)
设 \(f_{i, x}\) 表示使得前 \(i\) 个数合法且 \(b_i = x\) 的最小花费,考虑如何转移:
由于有 \(b_i + s_i \le b_{i + 1}\) 的限制,那么有:
考虑设 \(F_i(x) = f_{i, x}\),下面考虑如何从 \(F_i\) 转移到 \(F_{i + 1}\)。我们有:
记 \(G_i(x) = \min\limits_{y \le x} F_i(y)\),那么有:
使用 Slope Trick 即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::priority_queue<valueType, ValueVector, std::greater<valueType>> ValueMinHeap;
typedef std::priority_queue<valueType, ValueVector, std::less<valueType>> ValueMaxHeap;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
ValueVector A(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
valueType ans = 0, shifting = 0;
ValueMaxHeap Q;
for (valueType i = 1; i <= N; ++i) {
shifting -= std::max<valueType>(0, A[i] - A[i - 1]);
Q.push(shifting);
Q.push(A[i] + shifting);
ans += Q.top() - shifting;
ans += Q.top() - (A[i] + shifting);
Q.pop();
}
std::cout << ans << std::endl;
}