Solution Set【2024.1.4】
A. 永雏塔菲
首先可以发现,对于操作 \(2\),我们可以先通过操作 \(3\) 将该点的点权赋为极大值,后通过操作 \(1\) 查询到的答案便一定包含该点。
因此,我们可以将操作 \(2\) 转化为两次操作 \(3\) 和一次操作 \(1\),这样就可以将操作 \(2\) 去掉。
下面问题变为了:
给定一棵树,每个点有一个点权,有两种操作:
- 查询权值和最大的链的权值和
- 修改某个点的点权
带修树链统计问题考虑点分树。对于每个分治中心,我们可以对于其分治范围内的每个点维护出其到分治中心的距离,可以发现修改点权的操作在这里相当于是一个子树加操作,而查询贡献相当于是一个子树最大值操作。
因此对于点分树的每一层深度,我们对于按分治中心将分治范围内的节点建出 \(\tt{DFS}\) 序,然后建出线段树,维护其子树的最大值和子树的和。
发现对于每个分治中心需要选择其原树中所有儿子节点的贡献的最大值和次大值,因此我们可以对于每个点开一个 \(\tt{std::set}\) 维护原树中所有儿子节点的贡献,这样便可以计算每个分治中心的答案。
对于全局答案的统计可以使用一个可删堆维护,每次处理当前分治中心的修改时先将其原来的贡献删去,修改后再将其新的贡献加入。
时间复杂度 \(\mathcal{O}(n \log^2 n)\)。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::vector<bool> bitset;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::set<ValuePair, std::greater<>> PairSet;
typedef std::vector<PairSet> SetMatrix;
constexpr valueType MIN = std::numeric_limits<valueType>::min(), MAX = std::numeric_limits<valueType>::max();
template<typename T, typename _Compare, typename _Plus = std::plus<>>
class RangeAddMinMaxSegmentTree {
private:
T N;
std::vector<T> data, lazy;
private:
_Compare Compare;
_Plus Plus;
static constexpr T INIT_VALUE = _Compare()(1, 2) ? MAX : MIN;
T merge(T a, T b) {
return Compare(a, b) ? a : b;
}
void merge(T id) {
data[id] = merge(data[id << 1], data[id << 1 | 1]);
}
void push(T id) {
if (lazy[id] == 0)
return;
data[id << 1] = Plus(data[id << 1], lazy[id]);
data[id << 1 | 1] = Plus(data[id << 1 | 1], lazy[id]);
lazy[id << 1] = Plus(lazy[id << 1], lazy[id]);
lazy[id << 1 | 1] = Plus(lazy[id << 1 | 1], lazy[id]);
lazy[id] = 0;
}
public:
RangeAddMinMaxSegmentTree() = default;
explicit RangeAddMinMaxSegmentTree(T n, T initValue = INIT_VALUE) : N(n), data(n * 4 + 10, initValue), lazy(n * 4 + 10, 0) {}
RangeAddMinMaxSegmentTree(T n, std::vector<T> const &source) : N(n), data(n * 4 + 10), lazy(n * 4 + 10, 0) {
build(1, 1, N, source);
}
private:
void build(T id, T l, T r, std::vector<T> const &source) {
if (l == r) {
data[id] = source[l];
return;
}
T const mid = (l + r) >> 1;
build(id << 1, l, mid, source);
build(id << 1 | 1, mid + 1, r, source);
merge(id);
}
public:
void update(T l, T r, T x) {
if (l > r)
return;
update(1, 1, N, l, r, x);
}
void set(T pos, T key) {
set(1, 1, N, pos, key);
}
T query(T l, T r) {
if (l > r)
return INIT_VALUE;
return query(1, 1, N, l, r);
}
private:
void update(T id, T nodeL, T nodeR, T queryL, T queryR, T x) {
if (queryL <= nodeL && nodeR <= queryR) {
data[id] = Plus(data[id], x);
lazy[id] = Plus(lazy[id], x);
return;
}
push(id);
T const mid = (nodeL + nodeR) >> 1;
if (queryL <= mid)
update(id << 1, nodeL, mid, queryL, queryR, x);
if (queryR > mid)
update(id << 1 | 1, mid + 1, nodeR, queryL, queryR, x);
merge(id);
}
void set(T id, T nodeL, T nodeR, T pos, T key) {
if (nodeL == nodeR) {
data[id] = key;
return;
}
push(id);
T const mid = (nodeL + nodeR) >> 1;
if (pos <= mid)
set(id << 1, nodeL, mid, pos, key);
else
set(id << 1 | 1, mid + 1, nodeR, pos, key);
merge(id);
}
T query(T id, T nodeL, T nodeR, T queryL, T queryR) {
if (queryL <= nodeL && nodeR <= queryR)
return data[id];
push(id);
T const mid = (nodeL + nodeR) >> 1;
if (queryR <= mid)
return query(id << 1, nodeL, mid, queryL, queryR);
else if (queryL > mid)
return query(id << 1 | 1, mid + 1, nodeR, queryL, queryR);
else
return merge(query(id << 1, nodeL, mid, queryL, queryR), query(id << 1 | 1, mid + 1, nodeR, queryL, queryR));
}
};
template<typename T, typename _Compare>
class DeletableHeap {
public:
typedef std::priority_queue<T, std::vector<T>, _Compare> Heap;
private:
Heap heap, deleted;
public:
DeletableHeap() = default;
void push(const T &value) {
heap.push(value);
}
void erase(const T &value) {
deleted.push(value);
}
void pop() {
while (!deleted.empty() && heap.top() == deleted.top()) {
heap.pop();
deleted.pop();
}
heap.pop();
}
T top() {
while (!deleted.empty() && heap.top() == deleted.top()) {
heap.pop();
deleted.pop();
}
return heap.top();
}
size_t size() {
return heap.size() - deleted.size();
}
bool empty() {
return size() == 0;
}
void clear() {
heap.clear();
deleted.clear();
}
void swap(DeletableHeap &other) {
heap.swap(other.heap);
deleted.swap(other.deleted);
}
template<typename... _Args>
void emplace(_Args &&...__args) {
heap.emplace(std::forward<_Args>(__args)...);
}
};
valueType N, M;
ValueVector A;
ValueMatrix G;
ValueVector weight, size, father, up, depth;
ValueVector dfsCount;
ValueMatrix dist, leftBound, rightBound, node;
SetMatrix S;
bitset visited;
DeletableHeap<valueType, std::less<>> Q;
std::vector<RangeAddMinMaxSegmentTree<valueType, std::greater<>>> tree;
ValueVector max;
void calcSize(valueType x, valueType from, valueType const &S, valueType &ROOT) {
size[x] = 1;
weight[x] = std::numeric_limits<valueType>::min();
for (auto const &to : G[x]) {
if (to == from || visited[to])
continue;
calcSize(to, x, S, ROOT);
size[x] += size[to];
weight[x] = std::max(weight[x], size[to]);
}
weight[x] = std::max(weight[x], S - size[x]);
if (weight[x] < weight[ROOT])
ROOT = x;
}
void dfs(valueType x, valueType from, valueType dep, valueType &MAX) {
dist[dep][x] = dist[dep][from] + A[x];
leftBound[dep][x] = ++dfsCount[dep];
node[dep][dfsCount[dep]] = x;
MAX = std::max(MAX, dist[dep][x]);
for (auto const &to : G[x]) {
if (to == from || visited[to])
continue;
dfs(to, x, dep, MAX);
}
rightBound[dep][x] = dfsCount[dep];
}
valueType GetAns(valueType x) {
valueType sum = A[x];
sum += S[x].begin()->first;
if (S[x].size() > 1)
sum += std::max<valueType>(0, std::next(S[x].begin())->first);
return sum;
}
void InsertToAns(valueType x) {
valueType sum = A[x];
sum += S[x].begin()->first;
if (S[x].size() > 1)
sum += std::max<valueType>(0, std::next(S[x].begin())->first);
Q.push(sum);
}
void DeleteFromAns(valueType x) {
valueType sum = A[x];
sum += S[x].begin()->first;
if (S[x].size() > 1)
sum += std::max<valueType>(0, std::next(S[x].begin())->first);
Q.erase(sum);
}
void build(valueType x) {
visited[x] = true;
dist[depth[x]][x] = 0;
S[x].emplace(0, x);
for (auto const &to : G[x]) {
if (visited[to])
continue;
valueType MAX = MIN;
dfs(to, x, depth[x], MAX);
valueType ROOT = 0;
weight[ROOT] = std::numeric_limits<valueType>::max();
valueType const totalSize = size[to];
calcSize(to, x, totalSize, ROOT);
calcSize(ROOT, x, totalSize, ROOT);
S[x].emplace(MAX, ROOT);
father[ROOT] = x;
depth[ROOT] = depth[x] + 1;
up[ROOT] = to;
build(ROOT);
}
Q.push(max[x] = GetAns(x));
}
void insert(valueType x, valueType delta) {
{
Q.erase(max[x]);
A[x] += delta;
Q.push(max[x] = GetAns(x));
}
valueType last = x;
for (valueType y = father[x]; y != 0; last = y, y = father[y]) {
Q.erase(max[y]);
valueType d = depth[y];
S[y].erase({tree[d].query(leftBound[d][up[last]], rightBound[d][up[last]]), last});
tree[d].update(leftBound[d][x], rightBound[d][x], delta);
S[y].emplace(tree[d].query(leftBound[d][up[last]], rightBound[d][up[last]]), last);
Q.push(max[y] = GetAns(y));
}
}
valueType query() {
return Q.top();
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
#ifndef LOCAL_STDIO
freopen("taffy.in", "r", stdin);
freopen("taffy.out", "w", stdout);
#endif
std::cin >> N >> M;
valueType K = std::__lg(N) + 1;
A.resize(N + 1);
G.resize(N + 1);
weight.resize(N + 1);
size.resize(N + 1);
father.resize(N + 1);
up.resize(N + 1);
depth.resize(N + 1);
dist.resize(K + 1, ValueVector(N + 1));
leftBound.resize(K + 1, ValueVector(N + 1));
rightBound.resize(K + 1, ValueVector(N + 1));
node.resize(K + 1, ValueVector(N + 1));
dfsCount.resize(K + 1, 0);
S.resize(N + 1);
visited.resize(N + 1);
max.resize(N + 1);
for (valueType i = 1; i < N; ++i) {
valueType u, v;
std::cin >> u >> v;
G[u].push_back(v);
G[v].push_back(u);
}
for (valueType i = 1; i <= N; ++i)
std::cin >> A[i];
valueType ROOT = 0;
weight[ROOT] = std::numeric_limits<valueType>::max();
calcSize(1, 1, N, ROOT);
calcSize(ROOT, ROOT, N, ROOT);
build(ROOT);
{
K = 0;
for (valueType i = 1; i <= N; ++i)
K = std::max(K, depth[i]);
}
tree.resize(K + 1);
for (valueType d = 0; d <= K; ++d) {
ValueVector temp(N + 1);
for (valueType i = 1; i <= N; ++i)
temp[leftBound[d][i]] = dist[d][i];
tree[d] = RangeAddMinMaxSegmentTree<valueType, std::greater<>>(N, temp);
}
for (valueType m = 0; m < M; ++m) {
valueType type;
std::cin >> type;
if (type == 1) {
std::cout << query() << '\n';
} else if (type == 2) {
valueType x;
std::cin >> x;
constexpr valueType INF = 1ll << 50;
insert(x, INF);
std::cout << query() - INF << '\n';
insert(x, -INF);
} else {
valueType x, y;
std::cin >> x >> y;
insert(x, y - A[x]);
}
}
std::cout << std::flush;
return 0;
}
C. 耀眼
首先我们可以得到一个 \(\mathcal{O}(n^2)\) 的做法:记 \(f_{k, i}\) 表示和为 \(i\),末尾元素为 \(j\) 的答案,可以得到转移:
发现若首项元素过大时序列的最大长度很小,故考虑设阀值 \(B\)。对于首项元素小于 \(B\) 的情况,发现其序列最大值也是 \(\mathcal{O}(B + \sqrt{N})\) 级别的,因此可以使用上述复杂度在 \(\mathcal{O}(Bn)\) 的复杂度内解决。
对于首项元素大于等于 \(B\) 的情况,我们可以使用一个复杂度与序列长度相关的做法。
当我们取 \(B \ge \sqrt{n}\) 的时候,可以发现序列的最小值一定是正整数,因此我们没有了序列元素必须为正整数的限制,进而我们可以通过考虑查分意义来转移。
为了便于计算,我们使得元素的每个元素均减去其首项,设 \(g_{i, j}\) 表示长度为 \(i\),和为 \(j\) 的答案,可以得到转移:
由于序列的长度为 \(\mathcal{O}\left(\dfrac{n}{B}\right)\),其元素最大值为 \(\mathcal{O}\left(\left(\dfrac{n}{B}\right)^2\right)\),复杂度为 \(\mathcal{O}\left(\left(\dfrac{n}{B}\right)^3\right)\)。
取 \(B = \sqrt{n}\) 便可以得到复杂度为 \(\mathcal{O}(n^{\frac{3}{2}})\) 的做法。
Code
#include <bits/stdc++.h>
typedef int valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
namespace MODINT_WITH_FIXED_MOD {
constexpr valueType MOD = 998244353;
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;
constexpr valueType B = 650;
constexpr valueType V = 1000;
constexpr valueType S = 1023;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
#ifndef LOCAL_STDIO
freopen("dazzling.in", "r", stdin);
freopen("dazzling.out", "w", stdout);
#endif
valueType N, W;
std::cin >> N >> W;
static_assert(V <= S);
assert((V + B) * (V - B) > 2 * N);
ValueMatrix F(S + 1, ValueVector(S + 1, 0));
for (valueType i = 1; i <= N; ++i) {
std::fill(F[i & S].begin(), F[i & S].end(), 0);
if (i <= V)
F[i][i] = 1;
for (valueType j = 1; j <= V && j <= i; ++j) {
Inc(F[i & S][j], mul(F[(i - j) & S][j + 1], W));
Inc(F[i & S][j], F[(i - j) & S][j - 1]);
}
}
valueType const M = N;
ValueMatrix G(2, ValueVector(2 * M + 1, 0));
G[1][0 + M] = 1;
valueType ans = (N >= B ? 1 : 0);
for (valueType i = 2; i <= B; ++i) {
valueType const now = i & 1, prev = now ^ 1;
std::fill(G[now].begin(), G[now].end(), 0);
for (valueType j = 0; j <= 2 * M; ++j) {
if (j - (i - 1) >= 0)
Inc(G[now][j], mul(G[prev][j - (i - 1)], W));
if (j + (i - 1) <= 2 * M)
Inc(G[now][j], G[prev][j + (i - 1)]);
}
for (valueType j = B; j <= 2 * (N + M) / i; ++j)
if (N - j * i >= -M)
Inc(ans, G[now][N - j * i + M]);
}
for (valueType i = 1; i < B; ++i)
Inc(ans, F[N & S][i]);
std::cout << ans << std::endl;
return 0;
}
[IOI2000] 邮局
定义 \(f_{k, i}\) 表示在前 \(i\) 个点中建立 \(k\) 个邮局的最小代价,可以得到转移:
其中 \(w\left(l, r\right)\) 表示在 \(l\) 到 \(r\) 之间建立一个邮局的代价,取中位数计算即可。下面证明其满足四边形不等式。
首先,\(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)\)。
我们可以发现:
其中 \(x\) 为村庄的位置,假设其以按升序排列,\(m = \left\lfloor\frac{l + r}{2}\right\rfloor\)。
因此我们可以得出:
可以发现 \(x_l\) 为常量,\(\left\lfloor\frac{l + r}{2}\right\rfloor\) 随 \(y\) 的增长单调不减,因此 \(f_l\left(r\right)\) 随 \(r\) 的增长单调不减,即 \(w\left(l, r\right)\) 满足四边形不等式。
进而我们通过进行 \(k\) 次分治来计算 \(f_{k, i}\),复杂度为 \(\mathcal{O}(PV \log V)\)。
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();
void solve(valueType AnsL, valueType AnsR, valueType L, valueType R, ValueVector &F, ValueVector const &last, ValueMatrix 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, M;
std::cin >> N >> M;
ValueVector X(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
std::cin >> X[i];
std::sort(X.begin(), X.end());
ValueVector S(N + 1);
std::partial_sum(X.begin(), X.end(), S.begin());
ValueMatrix W(N + 1, ValueVector(N + 1, 0));
for (valueType l = 1; l <= N; ++l) {
for (valueType r = l; r <= N; ++r) {
valueType const mid = (l + r) >> 1;
W[l][r] = 0;
W[l][r] += (mid - l) * X[mid] - (S[mid - 1] - S[l - 1]);
W[l][r] += (S[r] - S[mid]) - (r - mid) * X[mid];
}
}
ValueMatrix F(M + 1, ValueVector(N + 1, 0));
F[1] = W[1];
for (valueType i = 2; i <= M; ++i)
solve(i, N, 1, N, F[i], F[i - 1], W);
std::cout << F[M][N] << std::endl;
return 0;
}
CF868F Yet Another Minimization Problem
设 \(f_{k, i}\) 表示将前 \(i\) 个元素分为 \(k\) 个区间的最小代价,可以得到转移:
其中 \(w\left(l, r\right)\) 表示 \(l\) 和 \(r\) 之间满足 \(a_i = a_j\) 的无序数对 \(\left(i, j\right)\) 的个数,即区间价值。下面考虑证明其满足四边形不等式。
首先,\(w\left(l, r\right)\) 满足四边形不等式,当且仅当:
我们考虑每个数对产生的贡献。
- 对于满足 \(l < i \le j < r\) 的数对 \(\left(i, j\right)\),可以发现其在不等式两侧均会被计算两次。
- 对于满足 \(l = i \land j < r\) 或 \(l < i \land j = r\) 的数对 \(\left(i, j\right)\),可以发现其在不等式两侧均会被计算一次。
- 对于数对 \(\left(l, r\right)\),其在不等式右侧不被计算,在不等式左侧会被计算一次。
因此不等式成立。
因此该转移满足决策单调性,考虑使用分治计算即可,发现其难点在于如何计算区间价值。
考虑使用类似于莫队的方法,我们维护两个指针,在需要查询区间价值的时候移动指针并维护指针代表的区间的答案。可以发现左指针的移动路径是在决策点的分治树上遍历,右指针的移动路径是在位置的分治树上遍历,且每次移动距离均为区间长度的 \(\mathcal{O}(1)\) 倍。
因此我们使用分治法计算答案,在计算的过程中维护左右指针来实现区间价值的计算,复杂度为 \(\mathcal{O}(n \log n)\)。
Code
#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) {
ans += bucket[x];
++bucket[x];
}
void Remove(valueType x) {
--bucket[x];
ans -= bucket[x];
}
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;
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;
}
[CmdOI2019] 任务分配问题
设 \(f_{k, i}\) 表示将前 \(i\) 个任务分为 \(k\) 段的最小代价,可以得到转移:
其中 \(w\left(l, r\right)\) 代表区间 \(\left[l, r\right]\) 内的升序对数量。
此类于数对贡献有关的四边形不等式分析问题和如何计算 \(w\left(l, r\right)\) 可参考 CF868F Yet Another Minimization Problem。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
template<typename T, class Operator = std::plus<>>
class TreeArray {
private:
T N;
std::vector<T> tree;
Operator op;
static T lowbit(T x) {
return x & (-x);
}
public:
TreeArray() = default;
explicit TreeArray(T n, T initValue = 0) : N(n), tree(n + 1, initValue) {}
void resize(T n) {
N = n;
tree.resize(n + 1);
std::fill(tree.begin(), tree.end(), 0);
}
void insert(T pos, T value) {
if (pos <= 0 || pos > N)
return;
while (pos <= N) {
tree[pos] = op(tree[pos], value);
pos += lowbit(pos);
}
}
T query(T pos) {
if (pos <= 0 || pos > N)
return 0;
T result = 0;
while (pos > 0) {
result = op(result, tree[pos]);
pos -= lowbit(pos);
}
return result;
}
};
class Recorder {
private:
valueType N, ans;
valueType l, r;
ValueVector A;
TreeArray<valueType> bucket;
void InsertLeft(valueType x) {
ans += bucket.query(N) - bucket.query(x);
bucket.insert(x, 1);
}
void RemoveLeft(valueType x) {
bucket.insert(x, -1);
ans -= bucket.query(N) - bucket.query(x);
}
void InsertRight(valueType x) {
ans += bucket.query(x - 1);
bucket.insert(x, 1);
}
void RemoveRight(valueType x) {
bucket.insert(x, -1);
ans -= bucket.query(x - 1);
}
void Move(valueType L, valueType R) {
while (l > L)
InsertLeft(A[--l]);
while (r < R)
InsertRight(A[++r]);
while (l < L)
RemoveLeft(A[l++]);
while (r > R)
RemoveRight(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.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;
}
[NOI2009] 诗人小G
设 \(f_{i}\) 表示将前 \(i\) 个元素分为若干段的最小代价,可以得到转移:
其中 \(w\left(l, r\right)\) 代表区间 \(\left[l, r\right]\) 内的句子划为一行的代价。
可以发现 \(w_0\left(l, r\right) = \sum\limits_{l \le i \le r} len_i + r - l - L\) 满足四边形恒等式,而 \(h_p\left(x\right) = x^p\) 为凸函数,因此 \(w\left(l, r\right) = h_p\left(w_0\left(l, r\right)\right)\) 满足四边形不等式。
使用单调队列维护决策点即可,复杂度为 \(\mathcal{O}(n \log n)\)。
对于答案过大情况的判断,我们可以使用浮点数类型来存储答案,然后在最后输出的时候判断是否超过 \(10^{18}\) 即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef long double realType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<realType> RealVector;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::deque<ValueTuple> TupleDeque;
typedef std::string string;
typedef std::vector<string> StringVector;
realType pow(realType a, valueType b) {
realType result = 1;
while (b > 0) {
if (b & 1)
result *= a;
a *= a;
b >>= 1;
}
return result;
}
constexpr realType MAX = std::numeric_limits<realType>::max();
constexpr realType LIMIT = 1e18;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType T;
std::cin >> T;
for (valueType testcase = 0; testcase < T; ++testcase) {
valueType N, P;
realType L;
std::cin >> N >> L >> P;
StringVector S(N + 1);
for (valueType i = 1; i <= N; ++i)
std::cin >> S[i];
ValueVector A(N + 1, 0);
for (valueType i = 1; i <= N; ++i)
A[i] = S[i].length();
ValueVector B(N + 1, 0);
std::partial_sum(A.begin(), A.end(), B.begin());
RealVector F(N + 1, MAX), Path(N + 1, 0);
F[0] = 0;
std::function<realType(valueType, valueType)> W = [&](valueType l, valueType r) {// weight of (l, r]
if (l >= r)
return MAX;
return F[l] + pow(std::fabs(L - (B[r] - B[l] + (r - l - 1))), P);
};
TupleDeque Q;
Q.emplace_back(0, 1, N);
for (valueType i = 1; i <= N; ++i) {
while (!Q.empty() && std::get<2>(Q.front()) < i)
Q.pop_front();
auto [p, l, r] = Q.front();
assert(l <= i && i <= r);
F[i] = W(p, i);
Path[i] = p;
if (i == N)
break;
std::get<1>(Q.front()) = std::max(std::get<1>(Q.front()), i + 1);
while (!Q.empty() && W(i, std::get<1>(Q.back())) < W(std::get<0>(Q.back()), std::get<1>(Q.back())))
Q.pop_back();
if (Q.empty()) {
Q.emplace_back(i, i + 1, N);
} else if (W(std::get<0>(Q.back()), std::get<2>(Q.back())) < W(i, std::get<2>(Q.back()))) {
auto [p, l, r] = Q.back();
if (r < N)
Q.emplace_back(i, r + 1, N);
} else {
auto const [p, l, r] = Q.back();
Q.pop_back();
valueType point = l;
valueType left = l, right = r;
while (left <= right) {
valueType const mid = (left + right) >> 1;
if (W(p, mid) <= W(i, mid)) {
point = mid;
left = mid + 1;
} else {
right = mid - 1;
}
}
Q.emplace_back(p, l, point);
if (point < N)
Q.emplace_back(i, point + 1, N);
}
}
if (F[N] > LIMIT) {
std::cout << "Too hard to arrange\n";
} else {
std::cout << (valueType) F[N] << '\n';
valueType i = N;
std::stack<StringVector> ans;
while (i > 0) {
valueType p = Path[i];
ans.emplace(StringVector(S.begin() + p + 1, S.begin() + i + 1));
i = p;
}
while (!ans.empty()) {
auto &s = ans.top();
std::cout << s[0];
for (valueType i = 1; i < s.size(); ++i)
std::cout << ' ' << s[i];
std::cout << '\n';
ans.pop();
}
}
std::cout << "--------------------\n";
}
}