Solution Set【2024.1.1】
实际上本文涵盖了 \(2023.12.30 \sim 2024.1.1\) 之间的题目。
[Ynoi2006] rldcot
考虑如下两个点对:
-
\(\operatorname{lca}(x, y) = \operatorname{lca}(a, b) = u\)
-
\(x \le a \le b \le y\)
容易发现,若 \(\left(x, y\right)\) 被选中,则 \(\left(a, b\right)\) 也一定被选中,称之为 \(\left(a, b\right)\) 支配了 \(\left(x, y\right)\)。
因此只需要统计出所有未被支配的点对即可。
考虑什么样的点对是未被支配的。
- 若 \(\operatorname{lca}(x, y) = u\),则 \(\left(x, y\right)\) 未被支配当且仅当不存在 \(x < z < y\) 且 \(\operatorname{lca}(x, z) = \operatorname{lca}(y, z) = u\)。
因此我们可以在固定 \(u\) 的情况下枚举 \(x\),在其他兄弟子树的点集中寻找前驱和后继,即可得到所有未被支配的点对。
这一过程可以通过树上启发式合并实现。具体的,维护一个按标号排序的有序点集,每次保留重儿子的点集,将其他点集合并到重儿子的点集中,在合并其他儿子的点集时统计点对。
每个点会被合并 \(\mathcal{O}(\log n)\) 次,每次会贡献 \(\mathcal{O}(1)\) 个点对,因此有效点对数量为 \(\mathcal{O}(n \log n)\),这部分的时间复杂度为 \(\mathcal{O}(n \log^2 n)\)。
接下来问题转化为如何统计答案。
首先我们将数对按最近公共祖先的深度值分组,那么询问 \(\left(l, r\right)\) 可以转化为询问有多少组点对存在点对 \(\left(x, y\right)\),满足 \(1 \le l \le x\) 且 \(y \le r \le n\)。
因此我们可以将点对 \(\left(x, y\right)\) 按 \(x\) 的值降序排序,然后维护一个树状数组和一个数组,树状数组用于维护答案,数组用于维护某个颜色 \(c\) 对应的出现过的最小 \(y\) 值,记为 \(last_c\)。那么每次插入一个点对 \(\left(x, y\right)\) 时,若 \(y < last_c\),那么就在树状数组上对区间 \(\left[y, last_c\right)\) 进行区间加操作,然后将 \(last_x\) 更新为 \(y\)。处理询问时单点查询即可。
总复杂度为 \(\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::set<valueType> ValueSet;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
typedef std::vector<TupleVector> TupleMatrix;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
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 insert(T pos, T value) {
#ifdef _UU_DEBUG
if (pos <= 0 || pos > N)
throw std::out_of_range("TreeArray::insert() : pos out of range");
#endif
while (pos <= N) {
tree[pos] = op(tree[pos], value);
pos += lowbit(pos);
}
}
T query(T pos) {
#ifdef _UU_DEBUG
if (pos <= 0 || pos > N)
throw std::out_of_range("TreeArray::query() : pos out of range");
#endif
T result = 0;
while (pos > 0) {
result = op(result, tree[pos]);
pos -= lowbit(pos);
}
return result;
}
};
valueType N, M;
PairMatrix G;
TupleVector pairs;
ValueSet S;
ValueVector dist, size, son, leftBound, rightBound, node;
void dfs(valueType x, valueType from) {
static valueType dfsCount = 0;
size[x] = 1;
son[x] = 0;
leftBound[x] = ++dfsCount;
node[dfsCount] = x;
for (auto const &[to, weight] : G[x]) {
if (to == from)
continue;
dist[to] = dist[x] + weight;
dfs(to, x);
size[x] += size[to];
if (son[x] == 0 || size[to] > size[son[x]])
son[x] = to;
}
rightBound[x] = dfsCount;
}
void calc(valueType x, valueType from, bool keep) {
for (auto const &[to, weight] : G[x])
if (to != son[x] && to != from)
calc(to, x, false);
if (son[x] != 0)
calc(son[x], x, true);
pairs.emplace_back(x, x, dist[x]);
S.insert(x);
for (auto const &[to, weight] : G[x]) {
if (to == from || to == son[x])
continue;
for (valueType i = leftBound[to]; i <= rightBound[to]; ++i) {
valueType y = node[i];
auto iter = S.upper_bound(y);
if (iter != S.end())
pairs.emplace_back(y, *iter, dist[x]);
if (iter != S.begin())
pairs.emplace_back(*std::prev(iter), y, dist[x]);
}
for (valueType i = leftBound[to]; i <= rightBound[to]; ++i)
S.insert(node[i]);
}
if (!keep)
S.clear();
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
std::cin >> N >> M;
G.resize(N + 1);
dist.resize(N + 1, 0);
size.resize(N + 1);
son.resize(N + 1);
leftBound.resize(N + 1);
rightBound.resize(N + 1);
node.resize(N + 1);
pairs.reserve(2 * N * std::__lg(N));
for (valueType i = 1; i < N; ++i) {
valueType u, v, w;
std::cin >> u >> v >> w;
G[u].emplace_back(v, w);
G[v].emplace_back(u, w);
}
dfs(1, 0);
ValueVector bucket(dist.begin() + 1, dist.end());
bucket.push_back(std::numeric_limits<valueType>::min());
std::sort(bucket.begin(), bucket.end());
bucket.erase(std::unique(bucket.begin(), bucket.end()), bucket.end());
for (auto &d : dist)
d = std::distance(bucket.begin(), std::lower_bound(bucket.begin(), bucket.end(), d));
calc(1, 0, true);
PairMatrix nodes(N + 1), query(N + 1);
for (auto const &[l, r, c] : pairs)
nodes[l].emplace_back(r, c);
for (valueType i = 0; i < M; ++i) {
valueType l, r;
std::cin >> l >> r;
query[l].emplace_back(r, i);
}
TreeArray<valueType> tree(N + 5);
ValueVector ans(M);
ValueVector last(N + 1, N + 1);
for (valueType l = N; l >= 1; --l) {
for (auto const &[r, c] : nodes[l]) {
if (r >= last[c])
continue;
tree.insert(r, 1);
tree.insert(last[c], -1);
last[c] = r;
}
for (auto const &[r, id] : query[l])
ans[id] = tree.query(r);
}
std::copy(ans.begin(), ans.end(), std::ostream_iterator<valueType>(std::cout, "\n"));
return 0;
}
「雅礼集训 2017 Day7」事情的相似度
首先可以通过对字符串建出后缀自动机后将问题放到 \(\tt{parent}\) 树上。这样问题就转化为了给定一个区间,求标号在这个区间内的点对的最近公共祖先的点权最大值。
类似与 [Ynoi2006] rldcot,统计出有效点对后扫描线使用线段树维护最大值即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::string string;
typedef std::vector<bool> bitset;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
typedef std::set<valueType> ValueSet;
template<typename T, class _Compare = std::less<>>
class MinMaxSegmentTree {
private:
T N;
std::vector<T> data, lazy;
_Compare Compare;
static constexpr T const INIT_VALUE = _Compare()(1, 2) ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min();
T merge(T const &a, T const &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) {
data[id << 1] = merge(data[id << 1], lazy[id]);
data[id << 1 | 1] = merge(data[id << 1 | 1], lazy[id]);
lazy[id << 1] = merge(lazy[id << 1], lazy[id]);
lazy[id << 1 | 1] = merge(lazy[id << 1 | 1], lazy[id]);
lazy[id] = INIT_VALUE;
}
public:
MinMaxSegmentTree() = default;
MinMaxSegmentTree(T n) : N(n), data(n * 4 + 10, _Compare()(1, 2) ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min()), lazy(n * 4 + 10, _Compare()(1, 2) ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min()) {}
MinMaxSegmentTree(T n, T initValue) : N(n), data(n * 4 + 10, initValue), lazy(n * 4 + 10, _Compare()(1, 2) ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min()) {}
explicit MinMaxSegmentTree(T n, std::vector<T> const &source) : N(n), data(n * 4 + 10), lazy(n * 4 + 10, _Compare()(1, 2) ? std::numeric_limits<T>::max() : std::numeric_limits<T>::min()) {
build(source, 1, 1, N);
}
private:
void build(T id, T l, T r, std::vector<T> const &source) {
if (l == r) {
data[id] = source[l];
return;
}
valueType 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 value) {
update(1, 1, N, l, r, value);
}
void update(T pos, T value) {
update(1, 1, N, pos, pos, value);
}
T query(T l, T r) {
return query(1, 1, N, l, r);
}
T query(T pos) {
return query(1, 1, N, pos, pos);
}
private:
void update(T id, T nodeL, T nodeR, T queryL, T queryR, T value) {
if (queryL <= nodeL && nodeR <= queryR) {
data[id] = merge(data[id], value);
lazy[id] = merge(lazy[id], value);
return;
}
push(id);
valueType const mid = (nodeL + nodeR) >> 1;
if (queryL <= mid)
update(id << 1, nodeL, mid, queryL, queryR, value);
if (queryR > mid)
update(id << 1 | 1, mid + 1, nodeR, queryL, queryR, value);
merge(id);
}
T query(T id, T nodeL, T nodeR, T queryL, T queryR) {
if (queryL <= nodeL && nodeR <= queryR)
return data[id];
push(id);
valueType const mid = (nodeL + nodeR) >> 1;
if (queryR <= mid)
return query(id << 1, nodeL, mid, queryL, queryR);
if (queryL > mid)
return query(id << 1 | 1, mid + 1, nodeR, queryL, queryR);
return merge(query(id << 1, nodeL, mid, queryL, queryR), query(id << 1 | 1, mid + 1, nodeR, queryL, queryR));
}
};
template<valueType CharSetSize, valueType BaseChar>
class SuffixAutomaton {
public:
typedef std::array<valueType, CharSetSize> TransferMap;
typedef std::vector<TransferMap> TransferMatrix;
private:
// main data
valueType N, UsedPoolSize;
ValueVector Link, Length;
TransferMatrix Transfer;
bitset Cloned;
// data for function extend
valueType Last;
// maybe useful
ValueVector TopoOrder;
ValueVector EndPosCount;
public:
SuffixAutomaton() = default;
explicit SuffixAutomaton(valueType n) : N(n), UsedPoolSize(N), Link(2 * N + 1), Length(2 * N + 1), Transfer(2 * N + 1), Cloned(2 * N + 1, false), Last(0) {
Link[0] = -1;
for (auto &p : Transfer)
p.fill(0);
}
explicit SuffixAutomaton(string const &s) : SuffixAutomaton(s.size()) {
for (size_t i = 0; i < s.size(); ++i)
extend(s[i] - BaseChar, i + 1);
}
explicit SuffixAutomaton(ValueVector const &s) : SuffixAutomaton(s.size()) {
for (size_t i = 0; i < s.size(); ++i)
extend(s[i], i + 1);
}
void extend(valueType ch, valueType New) {
Length[New] = Length[Last] + 1;
valueType P = Last;
Last = New;
while (P != -1 && !Transfer[P][ch]) {
Transfer[P][ch] = New;
P = Link[P];
}
if (P == -1) {
Link[New] = 0;
} else {
valueType Q = Transfer[P][ch];
if (Length[P] + 1 == Length[Q]) {
Link[New] = Q;
} else {
valueType Clone = ++UsedPoolSize;
Cloned[Clone] = true;
Length[Clone] = Length[P] + 1;
Link[Clone] = Link[Q];
Transfer[Clone] = Transfer[Q];
while (P != -1 && Transfer[P][ch] == Q) {
Transfer[P][ch] = Clone;
P = Link[P];
}
Link[Q] = Link[New] = Clone;
}
}
}
public:
// extra functions
void GetTopoOrder() {
TopoOrder.resize(UsedPoolSize + 1);
ValueVector Count(N + 1, 0);
for (valueType i = 0; i <= UsedPoolSize; ++i)
++Count[Length[i]];
for (valueType i = 1; i <= N; ++i)
Count[i] += Count[i - 1];
for (valueType i = 0; i <= UsedPoolSize; ++i)
TopoOrder[--Count[Length[i]]] = i;
std::reverse(TopoOrder.begin(), TopoOrder.end());
}
void GetEndPosCount() {
EndPosCount.resize(UsedPoolSize + 1);
std::fill(EndPosCount.begin(), EndPosCount.end(), 0);
for (valueType i = 0; i <= UsedPoolSize; ++i)
if (!Cloned[i])
EndPosCount[i] = 1;
for (auto const &u : TopoOrder)
if (Link[u] != -1)
EndPosCount[Link[u]] += EndPosCount[u];
EndPosCount[0] = 0;
}
valueType size() const {
return UsedPoolSize + 1;
}
valueType GetLength(valueType u) const {
return Length[u];
}
valueType GetLink(valueType u) const {
return Link[u];
}
};
namespace TREE {
valueType N, M;
ValueMatrix G;
TupleVector pairs;
ValueSet S;
ValueVector weight, size, son, leftBound, rightBound, node;
void dfs(valueType x, valueType from) {
static valueType dfsCount = 0;
size[x] = 1;
son[x] = 0;
leftBound[x] = ++dfsCount;
node[dfsCount] = x;
for (auto const &to : G[x]) {
if (to == from)
continue;
dfs(to, x);
size[x] += size[to];
if (son[x] == 0 || size[to] > size[son[x]])
son[x] = to;
}
rightBound[x] = dfsCount;
}
void calc(valueType x, valueType from, bool keep) {
for (auto const &to : G[x])
if (to != son[x] && to != from)
calc(to, x, false);
if (son[x] != 0)
calc(son[x], x, true);
{
auto iter = S.upper_bound(x);
if (iter != S.end())
pairs.emplace_back(x, *iter, weight[x]);
if (iter != S.begin())
pairs.emplace_back(*std::prev(iter), x, weight[x]);
S.insert(x);
}
for (auto const &to : G[x]) {
if (to == from || to == son[x])
continue;
for (valueType i = leftBound[to]; i <= rightBound[to]; ++i) {
valueType y = node[i];
auto iter = S.upper_bound(y);
if (iter != S.end())
pairs.emplace_back(y, *iter, weight[x]);
if (iter != S.begin())
pairs.emplace_back(*std::prev(iter), y, weight[x]);
}
for (valueType i = leftBound[to]; i <= rightBound[to]; ++i)
S.insert(node[i]);
}
if (!keep)
S.clear();
}
void init(valueType n, valueType m) {
N = n;
M = m;
G.resize(N + 10);
weight.resize(N + 10);
size.resize(N + 10);
son.resize(N + 10);
leftBound.resize(N + 10);
rightBound.resize(N + 10);
node.resize(N + 10);
pairs.reserve(N * std::__lg(N));
}
void addEdge(valueType u, valueType v) {
G[u].emplace_back(v);
G[v].emplace_back(u);
}
void setWeight(valueType u, valueType w) {
weight[u] = w;
}
void solve() {
dfs(1, 0);
calc(1, 0, true);
PairMatrix nodes(N + 1), query(N + 1);
for (auto const &[l, r, c] : pairs)
nodes[l].emplace_back(r, c);
for (valueType i = 0; i < M; ++i) {
valueType l, r;
std::cin >> l >> r;
++l;
++r;
query[l].emplace_back(r, i);
}
MinMaxSegmentTree<valueType, std::greater<>> tree(N + 5);
ValueVector ans(M);
for (valueType l = N; l >= 1; --l) {
for (auto const &[r, c] : nodes[l])
tree.update(r, N, c);
for (auto const &[r, id] : query[l])
ans[id] = tree.query(1, r);
}
for (auto &x : ans)
if (x < 0)
x = 0;
std::copy(ans.begin(), ans.end(), std::ostream_iterator<valueType>(std::cout, "\n"));
}
}// namespace TREE
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, M;
std::cin >> N >> M;
std::string S;
std::cin >> S;
SuffixAutomaton<2, '0'> SAM(S);
TREE::init(SAM.size(), M);
for (valueType i = 0; i < SAM.size(); ++i) {
TREE::setWeight(i + 1, SAM.GetLength(i));
if (SAM.GetLink(i) != -1)
TREE::addEdge(SAM.GetLink(i) + 1, i + 1);
}
TREE::solve();
return 0;
}
LOJ #6198. 谢特
首先对字符串建出后缀自动机后将问题放到 \(\tt{parent}\) 树上。
接下来问题便为点对贡献问题,对于点对间异或值的最大值可以使用 \(\tt{0-1 \, Trie}\) 解决。而最近公共祖先的权值贡献使用树上启发式合并即可。
也可以使用 \(\tt{Trie}\) 合并来在遍历树的过程中维护答案。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<ValueVector> ValueMatrix;
typedef std::string string;
typedef std::vector<bool> bitset;
typedef std::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
typedef std::set<valueType> ValueSet;
template<typename T, T MAXBIT>
class BinaryTrie {
private:
typedef std::array<T, 2> TransferArray;
typedef std::vector<TransferArray> TransferMatrix;
T UsedPoolSize, InsertCount;
TransferMatrix Transfer;
std::vector<T> count, sum;
void update(T current) {
count[current] = 0;
sum[current] = 0;
if (Transfer[current][0] != 0) {
count[current] += count[Transfer[current][0]];
sum[current] ^= sum[Transfer[current][0]] << 1;
}
if (Transfer[current][1] != 0) {
count[current] += count[Transfer[current][1]];
sum[current] ^= sum[Transfer[current][1]] << 1 | (count[Transfer[current][1]] & 1);
}
}
public:
BinaryTrie() = default;
BinaryTrie(T N) : UsedPoolSize(0), InsertCount(0), Transfer((N + 100) * MAXBIT), count((N + 100) * MAXBIT, 0), sum((N + 100) * MAXBIT, 0) {
for (auto &p : Transfer)
p.fill(0);
}
public:
void insert(T x) {
++InsertCount;
T current = 0;
for (T i = MAXBIT - 1; i >= 0; --i) {
T bit = (x >> i) & 1;
if (Transfer[current][bit] == 0)
Transfer[current][bit] = ++UsedPoolSize;
++count[current];
sum[current] ^= bit << i;
current = Transfer[current][bit];
}
++count[current];
sum[current] ^= x & 1;
}
void erase(T x) {
--InsertCount;
T current = 0;
for (T i = MAXBIT - 1; i >= 0; --i) {
T bit = (x >> i) & 1;
--count[current];
sum[current] ^= bit << i;
current = Transfer[current][bit];
}
}
T GetMax(T x) {
T current = 0, result = 0;
for (T i = MAXBIT - 1; i >= 0; --i) {
T const bit = (x >> i) & 1;
if (Transfer[current][bit ^ 1] != 0 && count[Transfer[current][bit ^ 1]] != 0) {
result |= 1 << i;
current = Transfer[current][bit ^ 1];
} else {
current = Transfer[current][bit];
}
}
return result;
}
void clear() {
std::fill(count.begin(), std::next(count.begin(), UsedPoolSize + 10), 0);
std::fill(sum.begin(), std::next(sum.begin(), UsedPoolSize + 10), 0);
for (T i = 0; i <= UsedPoolSize; ++i)
Transfer[i].fill(0);
UsedPoolSize = 0;
InsertCount = 0;
}
T size() const {
return InsertCount;
}
bool empty() const {
return InsertCount == 0;
}
};
typedef BinaryTrie<valueType, 18> Trie;
template<valueType CharSetSize, valueType BaseChar>
class SuffixAutomaton {
public:
typedef std::array<valueType, CharSetSize> TransferMap;
typedef std::vector<TransferMap> TransferMatrix;
private:
// main data
valueType N, UsedPoolSize;
ValueVector Link, Length;
TransferMatrix Transfer;
bitset Cloned;
ValueVector FirstPos;
// data for function extend
valueType Last;
// maybe useful
ValueVector TopoOrder;
ValueVector EndPosCount;
public:
SuffixAutomaton() = default;
explicit SuffixAutomaton(valueType n) : N(n), UsedPoolSize(0), Link(2 * N + 1), Length(2 * N + 1), Transfer(2 * N + 1), Cloned(2 * N + 1, false), FirstPos(2 * N + 1, 0), Last(0) {
Link[0] = -1;
for (auto &p : Transfer)
p.fill(0);
}
explicit SuffixAutomaton(string const &s) : SuffixAutomaton(s.size()) {
for (size_t i = 0; i < s.size(); ++i)
extend(s[i] - BaseChar, i + 1);
}
explicit SuffixAutomaton(ValueVector const &s) : SuffixAutomaton(s.size()) {
for (size_t i = 0; i < s.size(); ++i)
extend(s[i], i + 1);
}
void extend(valueType ch, valueType pos) {
valueType New = ++UsedPoolSize;
Length[New] = Length[Last] + 1;
FirstPos[New] = pos;
valueType P = Last;
Last = New;
while (P != -1 && !Transfer[P][ch]) {
Transfer[P][ch] = New;
P = Link[P];
}
if (P == -1) {
Link[New] = 0;
} else {
valueType Q = Transfer[P][ch];
if (Length[P] + 1 == Length[Q]) {
Link[New] = Q;
} else {
valueType Clone = ++UsedPoolSize;
Cloned[Clone] = true;
Length[Clone] = Length[P] + 1;
Link[Clone] = Link[Q];
Transfer[Clone] = Transfer[Q];
FirstPos[Clone] = FirstPos[Q];
while (P != -1 && Transfer[P][ch] == Q) {
Transfer[P][ch] = Clone;
P = Link[P];
}
Link[Q] = Link[New] = Clone;
}
}
}
public:
// extra functions
void GetTopoOrder() {
TopoOrder.resize(UsedPoolSize + 1);
ValueVector Count(N + 1, 0);
for (valueType i = 0; i <= UsedPoolSize; ++i)
++Count[Length[i]];
for (valueType i = 1; i <= N; ++i)
Count[i] += Count[i - 1];
for (valueType i = 0; i <= UsedPoolSize; ++i)
TopoOrder[--Count[Length[i]]] = i;
std::reverse(TopoOrder.begin(), TopoOrder.end());
}
void GetEndPosCount() {
EndPosCount.resize(UsedPoolSize + 1);
std::fill(EndPosCount.begin(), EndPosCount.end(), 0);
for (valueType i = 0; i <= UsedPoolSize; ++i)
if (!Cloned[i])
EndPosCount[i] = 1;
for (auto const &u : TopoOrder)
if (Link[u] != -1)
EndPosCount[Link[u]] += EndPosCount[u];
EndPosCount[0] = 0;
}
valueType size() const {
return UsedPoolSize + 1;
}
valueType GetLength(valueType u) const {
return Length[u];
}
valueType GetLink(valueType u) const {
return Link[u];
}
valueType GetFirstPos(valueType u) const {
return FirstPos[u];
}
};
class Tree {
private:
valueType N;
ValueMatrix G;
ValueVector weight, size, son, length;
ValueVector leftBound, rightBound, node;
Trie trie;
public:
Tree() = default;
explicit Tree(valueType n) : N(n), G(n + 1), weight(n + 1), size(n + 1), son(n + 1), length(n + 1), leftBound(n + 1), rightBound(n + 1), node(n + 1), trie(n + 1) {}
public:
void addEdge(valueType u, valueType v) {
G[u].push_back(v);
G[v].push_back(u);
}
void setWeight(valueType u, valueType w) {
weight[u] = w;
}
void setLength(valueType u, valueType l) {
length[u] = l;
}
valueType solve() {
valueType dfsCount = 0;
dfs(1, 0, dfsCount);
valueType ans = 0;
calc(1, 0, true, ans);
return ans;
}
private:
void dfs(valueType x, valueType from, valueType &dfsCount) {
size[x] = 1;
son[x] = 0;
leftBound[x] = ++dfsCount;
node[dfsCount] = x;
for (auto const &to : G[x]) {
if (to == from)
continue;
dfs(to, x, dfsCount);
size[x] += size[to];
if (son[x] == 0 || size[to] > size[son[x]])
son[x] = to;
}
rightBound[x] = dfsCount;
}
void calc(valueType x, valueType from, bool keep, valueType &ans) {
for (auto const &to : G[x]) {
if (to == from || to == son[x])
continue;
calc(to, x, false, ans);
}
if (son[x] != 0)
calc(son[x], x, true, ans);
if (!trie.empty() && x != 1)
ans = std::max(ans, length[x] + trie.GetMax(weight[x]));
if (x != 1)
trie.insert(weight[x]);
for (auto const &to : G[x]) {
if (to == from || to == son[x])
continue;
for (valueType i = leftBound[to]; i <= rightBound[to]; ++i) {
valueType y = node[i];
if (!trie.empty())
ans = std::max(ans, length[x] + trie.GetMax(weight[y]));
}
for (valueType i = leftBound[to]; i <= rightBound[to]; ++i) {
valueType y = node[i];
trie.insert(weight[y]);
}
}
if (!keep)
trie.clear();
}
};
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
string S;
std::cin >> S;
std::reverse(S.begin(), S.end());
ValueVector weight(N + 1);
for (valueType i = N; i >= 1; --i)
std::cin >> weight[i];
SuffixAutomaton<26, 'a'> SAM(S);
Tree tree(SAM.size());
for (valueType i = 1; i < SAM.size(); ++i)
tree.addEdge(SAM.GetLink(i) + 1, i + 1);
for (valueType i = 0; i < SAM.size(); ++i)
tree.setWeight(i + 1, weight[SAM.GetFirstPos(i)]);
for (valueType i = 0; i < SAM.size(); ++i)
tree.setLength(i + 1, SAM.GetLength(i));
std::cout << tree.solve() << std::endl;
return 0;
}
「Cfz Round 3」Circle
首先可以发现给出的函数实质上是在连边 \(i \rightarrow p_i\) 的图中走 \(k\) 步到达的点的标号。
由于 \(p\) 是一个排列,所以这个图是由若干个环组成的。
考虑有解的充要条件,即对于所有 \(S_i = 1\) 的 \(i\),其所在的环的长度 \(k\) 都满足 \(k \mid l\)。由于对于 \(S_i = 0\) 的 \(i\) 无限制,所以我们统计出所有 \(S_i = 1\) 的 \(i\) 的个数 \(C\),这样我们的问题就转化为了:
给定 \(l, C, n\),构造一个可重集,使得其元素均为 \(l\) 的因子,且元素和在 \(\left[C, n - 2\right] \cup \left\{n\right\}\) 之间。
元素和不能为 \(n - 1\) 是因为这样会剩余一个长度为 \(1\) 的环,而题目中限制了 \(p_i \neq i\),因此这样的环是不合法的。
可以发现,若其元素为合数,那么一定可以通过若干质因子构造出来,因此我们可以枚举质因子,然后判断其是否可以存在在集合中。那么可以发现可以在集合中的数只有 \(\mathcal{O}(\log l)\) 种,因此我们可以进行完全背包,进而可以得到方案,时间复杂度为 \(\mathcal{O}(n \log l)\),可以通过。
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::set<valueType> ValueSet;
typedef std::map<valueType, valueType> ValueMap;
class LineSieve {
public:
typedef LineSieve self;
typedef long long valueType;
typedef std::vector<valueType> container;
private:
valueType size;
container minFactorList;
container primeList;
public:
explicit LineSieve(valueType _size_) : size(_size_), minFactorList(_size_ + 1) {
for (valueType i = 2; i <= size; ++i) {
if (minFactorList[i] == 0) {
primeList.push_back(i);
minFactorList[i] = i;
}
for (auto const &iter : primeList) {
valueType const t = i * iter;
if (t > size)
break;
minFactorList[t] = iter;
if (i % iter == 0)
break;
}
}
}
valueType minFactor(valueType x) const {
if (x > size)
throw std::range_error("Larger than Size.");
if (x < 1)
throw std::range_error("Too small.");
return minFactorList[x];
}
bool isPrime(valueType x) const {
if (x > size)
throw std::range_error("Larger than Size.");
if (x < 1)
throw std::range_error("Too small.");
return minFactorList[x] == x;
}
valueType maxFactor(valueType x) const {
if (x > size)
throw std::range_error("Larger than Size.");
if (x < 1)
throw std::range_error("Too small.");
return isPrime(x) ? x : x / minFactorList[x];
}
const container &getPrime() const {
return primeList;
}
};
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, L;
std::cin >> N >> L;
valueType C = 0;
bitset limit(N + 1, false);
for (valueType i = 1; i <= N; ++i) {
char c;
std::cin >> c;
if (c == '1') {
++C;
limit[i] = true;
}
}
if (L == 0 || L == 1) {
if (C > 0 && L == 1) {
std::cout << -1 << '\n';
continue;
} else {
for (valueType i = 1; i <= N; ++i)
std::cout << (i % N + 1) << ' ';
std::cout << '\n';
continue;
}
continue;
}
ValueVector const prime = LineSieve(N).getPrime();
ValueVector factor;
for (auto const &iter : prime) {
if (L % iter == 0)
factor.push_back(iter);
}
bitset F(N + 1, false);
ValueVector path(N + 1, 0);
F[0] = true;
for (auto const &x : factor) {
for (valueType i = x; i <= N; ++i) {
if (F[i - x]) {
F[i] = true;
path[i] = x;
}
}
}
ValueVector pool;
valueType ans = -1;
for (valueType i = N; i >= C; --i) {
if (F[i] && i != N - 1) {
pool.emplace_back(i);
}
}
if (pool.empty()) {
std::cout << -1 << '\n';
continue;
}
std::shuffle(pool.begin(), pool.end(), std::mt19937(std::random_device()()));
ans = pool[0];
ValueVector B;
for (valueType i = ans; i > 0; i -= path[i])
B.push_back(path[i]);
if (N != ans)
B.push_back(N - ans);
ValueVector queue;
for (valueType i = 1; i <= N; ++i)
if (limit[i])
queue.push_back(i);
for (valueType i = 1; i <= N; ++i)
if (!limit[i])
queue.push_back(i);
valueType pointer = 0;
ValueVector P(N + 1, 0);
for (auto const size : B) {
if (size == 0)
continue;
ValueVector temp(queue.begin() + pointer, queue.begin() + pointer + size);
for (valueType i = 0; i + 1 < size; ++i)
P[temp[i]] = temp[i + 1];
P[temp[size - 1]] = temp[0];
pointer += size;
}
for (valueType i = 1; i <= N; ++i)
std::cout << P[i] << ' ';
std::cout << '\n';
}
std::cout << std::flush;
return 0;
}
[LNOI2022] 串
可以发现,将 \(T_0\) 设为空串,\(T_1\) 为 \(S_{1 \dots 1}\),之后每次将左右端点 \(l, r\) 执行 \(l \leftarrow l + 1, r \leftarrow r + 2\) 便可以得到一个 \(\left\lfloor\dfrac{N}{2}\right\rfloor\) 的构造,进而确定了答案下界。
考虑如何优化答案,发现若一个子串在 \(S\) 中出现两次,设其第一次出现右端点为 \(r\),长度为 \(len\),那么可以得到一个 \(len + \left\lfloor\dfrac{N - r}{2}\right\rfloor\) 的构造,对所有出现多次的子串求出最大值即可,这一部分可以使用后缀自动机实现。
同时我们只需要考虑最后一次跳转,因为在最后一次跳转前的过程可以认为每次字符串的长度均会加一,所以在这个过程中无论如何跳转都不会影响答案。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
typedef std::vector<bool> bitset;
typedef std::string string;
template<valueType CharSetSize, valueType BaseChar>
class SuffixAutomaton {
public:
typedef std::array<valueType, CharSetSize> TransferMap;
typedef std::vector<TransferMap> TransferMatrix;
private:
// main data
valueType N, UsedPoolSize;
ValueVector Link, Length;
TransferMatrix Transfer;
bitset Cloned;
ValueVector FirstPos;
// data for function extend
valueType Last;
// maybe useful
ValueVector TopoOrder;
ValueVector EndPosCount;
public:
SuffixAutomaton() = default;
explicit SuffixAutomaton(valueType n) : N(n), UsedPoolSize(0), Link(2 * N + 1), Length(2 * N + 1), Transfer(2 * N + 1), Cloned(2 * N + 1, false), FirstPos(2 * N + 1, 0), Last(0) {
Link[0] = -1;
for (auto &p : Transfer)
p.fill(0);
}
explicit SuffixAutomaton(string const &s) : SuffixAutomaton(s.size()) {
for (size_t i = 0; i < s.size(); ++i)
extend(s[i] - BaseChar, i + 1);
}
explicit SuffixAutomaton(ValueVector const &s) : SuffixAutomaton(s.size()) {
for (size_t i = 0; i < s.size(); ++i)
extend(s[i], i + 1);
}
void extend(valueType ch, valueType pos) {
valueType New = ++UsedPoolSize;
Length[New] = Length[Last] + 1;
FirstPos[New] = pos;
valueType P = Last;
Last = New;
while (P != -1 && !Transfer[P][ch]) {
Transfer[P][ch] = New;
P = Link[P];
}
if (P == -1) {
Link[New] = 0;
} else {
valueType Q = Transfer[P][ch];
if (Length[P] + 1 == Length[Q]) {
Link[New] = Q;
} else {
valueType Clone = ++UsedPoolSize;
Cloned[Clone] = true;
Length[Clone] = Length[P] + 1;
Link[Clone] = Link[Q];
Transfer[Clone] = Transfer[Q];
FirstPos[Clone] = FirstPos[Q];
while (P != -1 && Transfer[P][ch] == Q) {
Transfer[P][ch] = Clone;
P = Link[P];
}
Link[Q] = Link[New] = Clone;
}
}
}
public:
// extra functions
void GetTopoOrder() {
TopoOrder.resize(UsedPoolSize + 1);
ValueVector Count(N + 1, 0);
for (valueType i = 0; i <= UsedPoolSize; ++i)
++Count[Length[i]];
for (valueType i = 1; i <= N; ++i)
Count[i] += Count[i - 1];
for (valueType i = 0; i <= UsedPoolSize; ++i)
TopoOrder[--Count[Length[i]]] = i;
std::reverse(TopoOrder.begin(), TopoOrder.end());
}
void GetEndPosCount() {
EndPosCount.resize(UsedPoolSize + 1);
std::fill(EndPosCount.begin(), EndPosCount.end(), 0);
for (valueType i = 0; i <= UsedPoolSize; ++i)
if (!Cloned[i])
EndPosCount[i] = 1;
for (auto const &u : TopoOrder)
if (Link[u] != -1)
EndPosCount[Link[u]] += EndPosCount[u];
EndPosCount[0] = 0;
}
valueType size() const {
return UsedPoolSize + 1;
}
valueType GetLength(valueType u) const {
return Length[u];
}
valueType GetLink(valueType u) const {
return Link[u];
}
valueType GetFirstPos(valueType u) const {
return FirstPos[u];
}
public:
// customed functions for selected problems (P8368)
valueType Ans() const {
valueType ans = N / 2;
for (valueType i = 1; i <= UsedPoolSize; ++i)
if (EndPosCount[i] > 1)
ans = std::max(ans, Length[i] + (N - FirstPos[i]) / 2);
return ans;
}
};
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) {
string S;
std::cin >> S;
SuffixAutomaton<26, 'a'> SAM(S);
SAM.GetTopoOrder();
SAM.GetEndPosCount();
std::cout << SAM.Ans() << '\n';
}
return 0;
}
CF103E Buying Sets
由于已经满足对于任意 \(k\) 个集合的大小不小于 \(k\)。因此只需要保证对于任意 \(k\) 个集合的大小不大于 \(k\) 即可满足题目限制要求。
这也就是意味着只要选择了一个元素,那么就必须选择一个集合,否则该解就是不合法的。
不妨将集合的权值均增加一个较大值,同时让每个元素的权值均减少一个较大值,这里的较大值要满足任意答案减去这个值均不会是最优解。
剩下的问题就是如何求出最优解,由于元素有负权值,因此是有最小权闭合子图解决即可。
Code
#include <bits/stdc++.h>
typedef long long valueType;
typedef std::vector<valueType> ValueVector;
class Dinic {
private:
struct Edge {
public:
typedef std::list<Edge> container;
typedef container::iterator iterator;
valueType to;
valueType cap;
valueType flow;
iterator pair;
Edge() : to(-1), cap(-1), flow(-1), pair(){};
Edge(valueType to, valueType cap, iterator pair = iterator()) : to(to), cap(cap), flow(0), pair(pair){};
};
typedef std::vector<Edge::container> Graph;
typedef std::vector<Edge::iterator> IterVector;
valueType N;
Graph G;
ValueVector depth;
IterVector start;
bool Initialized;
public:
explicit Dinic(valueType N) : N(N), G(N + 1), depth(N + 1, 0), start(N + 1), Initialized(false){};
void addEdge(valueType from, valueType to, valueType cap) {
if (__builtin_expect(Initialized, false))
throw std::runtime_error("Dinic: addEdge after init");
G[from].emplace_back(to, cap);
G[to].emplace_back(from, 0);
G[from].back().pair = std::prev(G[to].end());
G[to].back().pair = std::prev(G[from].end());
}
void init() {
if (__builtin_expect(Initialized, false))
throw std::runtime_error("Dinic: init twice");
Initialized = true;
std::fill(depth.begin(), depth.end(), 0);
for (valueType i = 1; i <= N; ++i)
start[i] = G[i].begin();
}
void reset() {
if (__builtin_expect(!Initialized, false))
throw std::runtime_error("Dinic: reset before init");
for (valueType i = 1; i <= N; ++i)
for (auto &iter : G[i])
iter.flow = 0;
std::fill(depth.begin(), depth.end(), 0);
Initialized = false;
}
valueType maxFlow(valueType S, valueType T) {
if (__builtin_expect(!Initialized, false))
throw std::runtime_error("Dinic: maxFlow before init");
valueType result = 0;
while (bfs(S, T)) {
IterVector begin = start;
result += dfs(S, T, std::numeric_limits<valueType>::max(), begin);
}
return result;
}
private:
bool bfs(valueType S, valueType T) {
std::fill(depth.begin(), depth.end(), 0);
std::queue<valueType> Q;
Q.push(S);
depth[S] = 1;
while (!Q.empty()) {
valueType const u = Q.front();
Q.pop();
for (auto const &e : G[u]) {
if ((e.cap > e.flow) && (!depth[e.to])) {
depth[e.to] = depth[u] + 1;
Q.push(e.to);
}
}
}
return depth[T] > 0;
}
valueType dfs(valueType u, valueType T, valueType flow, IterVector &Begin) {
if (u == T || flow == 0)
return flow;
valueType result = 0;
for (auto &e = Begin[u]; e != G[u].end(); ++e) {
if (e->cap > e->flow && depth[e->to] == depth[u] + 1) {
valueType const f = dfs(e->to, T, std::min(flow - result, e->cap - e->flow), Begin);
e->flow += f;
e->pair->flow -= f;
result += f;
if (result == flow)
return flow;
}
}
return result;
}
};
constexpr valueType INF = std::numeric_limits<valueType>::max(), MAX = 1ll << 30;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
ValueVector set(N + 1, 0), item(N + 1, 0);
valueType size = 0;
valueType const S = ++size, T = ++size;
for (valueType i = 1; i <= N; ++i) {
set[i] = ++size;
item[i] = ++size;
}
Dinic dinic(size);
valueType sum = 0;
for (valueType i = 1; i <= N; ++i) {
valueType k;
std::cin >> k;
for (valueType j = 0; j < k; ++j) {
valueType x;
std::cin >> x;
dinic.addEdge(set[i], item[x], INF);
}
}
for (valueType i = 1; i <= N; ++i)
dinic.addEdge(item[i], T, MAX);
for (valueType i = 1; i <= N; ++i) {
valueType weight;
std::cin >> weight;
dinic.addEdge(S, set[i], MAX - weight);
sum += MAX - weight;
}
dinic.init();
std::cout << -(sum - dinic.maxFlow(S, T)) << std::endl;
return 0;
}
[HNOI2008] 玩具装箱
设 \(f_i\) 表示前 \(i\) 个箱子的最优解,那么有转移:
其中 \(s_i = i + \sum\limits_{j \le i} c_j\),\(L\) 较题目给出值大 \(1\)。
上式可以整理为:
使用斜率优化即可。
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;
realType GetSlope(ValuePair const &A, ValuePair const &B) {
return (realType) (B.second - A.second) / (B.first - A.first);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, L;
std::cin >> N >> L;
L = L + 1;
ValueVector C(N + 1, 0), S(N + 1, 0);
for (valueType i = 1; i <= N; ++i) {
std::cin >> C[i];
S[i] = S[i - 1] + C[i] + 1;
}
ValueVector F(N + 1, 0);
PairVector Q;
Q.reserve(N + 1);
Q.emplace_back(S[0], F[0] + S[0] * S[0]);
valueType pointer = 0;
for (valueType i = 1; i <= N; ++i) {
valueType const K = 2 * (S[i] - L);
while (pointer + 1 < Q.size() && GetSlope(Q[pointer], Q[pointer + 1]) < K)
++pointer;
auto const &[x, y] = Q[pointer];
F[i] = y - x * K + (S[i] - L) * (S[i] - L);
ValuePair const Node(S[i], F[i] + S[i] * S[i]);
while (Q.size() > 1 && GetSlope(Q[Q.size() - 2], Q[Q.size() - 1]) > GetSlope(Q[Q.size() - 1], Node))
Q.pop_back();
Q.push_back(Node);
}
std::cout << F[N] << std::endl;
return 0;
}
[APIO2010] 特别行动队
设 \(f_i\) 表示前 \(i\) 个人的最优解,那么有转移:
其中 \(s_i = \sum\limits_{j \le i} x_j\)。
上式可以整理为:
使用斜率优化即可。
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);
}
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);
}
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);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N, A, B, C;
std::cin >> N >> A >> B >> C;
ValueVector W(N + 1, 0), S(N + 1, 0);
for (valueType i = 1; i <= N; ++i) {
std::cin >> W[i];
S[i] = S[i - 1] + W[i];
}
ValueVector F(N + 1, 0);
PairVector Q;
Q.reserve(N + 1);
Q.emplace_back(2 * A * S[0], F[0] + A * S[0] * S[0] - B * S[0]);
valueType pointer = 0;
for (valueType i = 1; i <= N; ++i) {
valueType const K = S[i];
while (pointer + 1 < Q.size() && CompareGreater(Q[pointer], Q[pointer + 1], K))
++pointer;
auto const &[x, y] = Q[pointer];
F[i] = y - x * K + A * S[i] * S[i] + B * S[i] + C;
ValuePair const Node(2 * A * S[i], F[i] + A * S[i] * S[i] - B * S[i]);
while (Q.size() > 1 && CompareLess(Q[Q.size() - 2], Q[Q.size() - 1], Node))
Q.pop_back();
Q.push_back(Node);
}
std::cout << F[N] << std::endl;
return 0;
}
[ZJOI2007] 仓库建设
设 \(f_i\) 表示前 \(i\) 个点的最优解,那么有转移:
其中 \(sp_i = \sum\limits_{j \le i} p_j, s_i = \sum\limits_{j \le i} p_j \cdot x_j\)。
上式可以整理为:
使用斜率优化即可。
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);
}
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);
}
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);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
valueType N;
std::cin >> N;
ValueVector X(N + 1, 0), P(N + 1, 0), C(N + 1, 0), S(N + 1, 0);
for (valueType i = 1; i <= N; ++i) {
std::cin >> X[i] >> P[i] >> C[i];
S[i] = S[i - 1] + X[i] * P[i];
}
std::partial_sum(P.begin(), P.end(), P.begin());
ValueVector F(N + 1, 0);
PairVector Q;
Q.reserve(N + 1);
Q.emplace_back(P[0], F[0] + S[0]);
valueType pointer = 0;
for (valueType i = 1; i <= N; ++i) {
valueType const K = X[i];
while (pointer + 1 < Q.size() && CompareLess(Q[pointer], Q[pointer + 1], K))
++pointer;
auto const &[x, y] = Q[pointer];
F[i] = y - x * K + P[i] * X[i] + C[i] - S[i];
ValuePair const Node(P[i], F[i] + S[i]);
while (Q.size() > 1 && CompareLess(Q[Q.size() - 2], Q[Q.size() - 1], Node))
Q.pop_back();
Q.push_back(Node);
}
valueType ans = std::numeric_limits<valueType>::max();
for (valueType i = 1; i <= N; ++i)
if (P[i] == P[N])
ans = std::min(ans, F[i]);
std::cout << ans << std::endl;
return 0;
}
[USACO08MAR] Land Acquisition G
由于所有的土地都需要购买,所以若某块土地的长和宽均小于某块其他土地,那么这块土地一定会被包含在其他土地中。
因此我们可以将所有的土地按长 \(h_i\) 的值递减排序,若土地 \(i\) 满足 \(\forall j < i, w_j < w_i\),那么这块土地不会被并购,其中 \(w_i\) 表示土地 \(i\) 的宽。
这样我们可以处理出一个土地序列,其长单调不增,宽单调不减。
这样若我们选择了一个区间 \(\left[l, r\right]\) 的土地,那么其花费即为 \(h_l \times w_r\),进而可以进行动态规划。
设 \(f_i\) 表示前 \(i\) 个土地的最优解,那么有转移:
使用斜率优化即可。
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;
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);
}
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);
}
bool CompareLess(ValuePair const &A, ValuePair const &B, valueType const K) {
if ((B.second - A.second) * (B.first - A.first) < 0 && K > 0)
return true;
else if ((B.second - A.second) * (B.first - A.first) > 0 && K < 0)
return false;
if ((B.second - A.second) < 0 && (B.first - A.first) < 0)
return (B.second - A.second) > K * (B.first - A.first);
else
return (B.second - A.second) < K * (B.first - A.first);
}
bool CompareGreater(ValuePair const &A, ValuePair const &B, valueType const K) {
if ((B.second - A.second) * (B.first - A.first) < 0 && K > 0)
return false;
else if ((B.second - A.second) * (B.first - A.first) > 0 && K < 0)
return true;
if ((B.second - A.second) < 0 && (B.first - A.first) < 0)
return (B.second - A.second) < K * (B.first - A.first);
else
return (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;
std::cin >> N;
PairVector A(N);
for (auto &[x, y] : A)
std::cin >> x >> y;
std::sort(A.begin(), A.end(), std::greater<>());
PairVector B({ValuePair(1ll << 25, 0)});
B.reserve(N);
for (auto const &[x, y] : A) {
if (B.empty() || B.back().second < y)
B.emplace_back(x, y);
}
N = B.size() - 1;
ValueVector H(N + 10, 1ll << 25), W(N + 10, 1ll << 25);
for (valueType i = 0; i <= N; ++i)
H[i] = B[i].first, W[i] = B[i].second;
ValueVector F(N + 1, 0);
PairVector Q;
Q.reserve(N + 1);
Q.emplace_back(-H[1], F[0]);
valueType pointer = 0;
for (valueType i = 1; i <= N; ++i) {
valueType const K = W[i];
while (pointer + 1 < Q.size() && CompareLess(Q[pointer], Q[pointer + 1], K))
++pointer;
auto const &[x, y] = Q[pointer];
F[i] = y - x * K;
ValuePair const Node(-H[i + 1], F[i]);
while (Q.size() > 1 && CompareLess(Q[Q.size() - 2], Q[Q.size() - 1], Node)) {
Q.pop_back();
pointer = std::max<valueType>(pointer - 1, 0);
}
Q.push_back(Node);
}
std::cout << F[N] << std::endl;
return 0;
}