Solution Set【2024.1.18】
A. 树上横跳
发现可以将问题转化为将路径 \(s \rightarrow t\) 划分为若干段,每段的贡献为段首元素的编号乘上段长,然后求和。
发现其可以通过自底而上维护一个单调递减的栈来解决,考虑如何处理多组询问。
考虑倍增,这样路径就划分为了 \(\mathcal{O}(\log n)\) 段,我们考虑如何将这些段合并起来。由于我们是将区间倍增得来的,因此对于每个长度大于 \(1\) 的区间我们均可以将其划分为两个子区间,我们按其左侧的最小值进行分类讨论:
- 若左侧的最小值大于该区间左端点,直接输出该区间内的单调栈的答案即可。
- 若左侧的最小值小于等于该区间的最小值,那么这个区间内的全部元素均可以被左侧的最小值所覆盖,因此我们可以将其合并到左侧的区间中。
- 否则考虑左侧的子区间,若其会被覆盖,那么递归的合并左区间,否则合并右区间。
复杂度为 \(\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::pair<valueType, valueType> ValuePair;
typedef std::vector<ValuePair> PairVector;
typedef std::vector<PairVector> PairMatrix;
typedef std::stack<valueType> ValueStack;
valueType N, M, K;
PairMatrix G;
ValueVector dist, depth, leftBound, rightBound;
ValueMatrix Father, F, Min;
valueType merge(valueType left, valueType x, valueType k) {
if (Min[k][x] >= left)
return left * (dist[x] - dist[Father[k][x]]);
if (left > Father[k][x])
return F[k][x];
valueType const y = Father[k - 1][x];
if (Min[k - 1][y] >= left)
return left * (dist[y] - dist[Father[k][x]]) + merge(left, x, k - 1);
else
return F[k][x] - F[k - 1][y] + merge(left, y, k - 1);
}
void dfs(valueType x, valueType from) {
static valueType dfsCount = 0;
Father[0][x] = from;
Min[0][x] = from;
depth[x] = depth[from] + 1;
leftBound[x] = ++dfsCount;
for (auto const &[to, weight] : G[x]) {
if (to == from)
continue;
dist[to] = dist[x] + weight;
F[0][to] = x * weight;
dfs(to, x);
}
rightBound[x] = dfsCount;
}
void build(valueType x, valueType from) {
for (valueType i = 1; i <= K; ++i) {
Father[i][x] = Father[i - 1][Father[i - 1][x]];
if (Father[i][x] == 0)
break;
Min[i][x] = std::min(Min[i - 1][x], Min[i - 1][Father[i - 1][x]]);
F[i][x] = F[i - 1][Father[i - 1][x]] + merge(Min[i - 1][Father[i - 1][x]], x, i - 1);
}
for (auto const &[to, weight] : G[x]) {
if (to == from)
continue;
build(to, x);
}
}
bool InTree(valueType u, valueType v) {// check if v is in the subtree of u
return leftBound[u] <= leftBound[v] && rightBound[v] <= rightBound[u];
}
bool isChain() {
for (valueType i = 1; i <= N; ++i) {
if (G[i].size() > 2)
return false;
}
return true;
}
namespace Chain {// 离线询问,时空复杂度 O(n log n + m)
void solve() {
PairMatrix Q(N + 1);
ValueVector Ans(M);
for (valueType m = 0; m < M; ++m) {
valueType u, v;
std::cin >> u >> v;
if (!InTree(u, v))
Ans[m] = -1;
else if (u == v)
Ans[m] = 0;
else
Q[u].emplace_back(v, m);
}
valueType Leaf = 1;
for (valueType i = 2; i <= N; ++i) {
if (depth[i] > depth[Leaf])
Leaf = i;
}
PairVector stack;// first:节点编号,second:从栈底到该节点的带权路径和
stack.reserve(N);
stack.emplace_back(Leaf, 0);
// for (auto const &[v, id] : Q[Leaf]) {
// if (v == Leaf)
// Ans[id] = 0;
// else
// std::abort();
// }
assert(Q[Leaf].empty());
valueType x = Father[0][Leaf];
while (x != 0) {
while (stack.size() > 1 && stack.back().first > x) {
stack.pop_back();
}
stack.emplace_back(x, stack.back().second + (dist[stack.back().first] - dist[x]) * x);
for (auto const &[v, id] : Q[x]) {
valueType l = 0, r = stack.size() - 1, pos = 0;
while (l <= r) {
valueType const mid = (l + r) / 2;
if (InTree(stack[mid].first, v)) {
pos = mid;
r = mid - 1;
} else {
l = mid + 1;
}
}
Ans[id] = stack.back().second - stack[pos].second + (dist[v] - dist[stack[pos].first]) * stack[pos].first;
}
x = Father[0][x];
}
for (auto const &ans : Ans)
std::cout << ans << '\n';
std::cout << std::flush;
}
}// namespace Chain
valueType solve(valueType u, valueType v) {
if (!InTree(u, v))
return -1;
if (u == v)
return 0;
valueType const difference = depth[v] - depth[u];
PairVector stack;
stack.reserve(K);
for (valueType i = 0; i <= K; ++i) {
if ((difference >> i) & 1) {
stack.emplace_back(v, i);
v = Father[i][v];
}
}
std::reverse(stack.begin(), stack.end());
valueType ans = 0, min = std::numeric_limits<valueType>::max();
for (auto const &[x, k] : stack) {
ans += merge(min, x, k);
min = std::min(min, Min[k][x]);
}
return ans;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
#ifndef LOCAL_STDIO
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
#endif
std::cin >> N >> M;
K = std::__lg(N);
G.resize(N + 1);
dist.resize(N + 1);
depth.resize(N + 1);
leftBound.resize(N + 1);
rightBound.resize(N + 1);
Father.resize(K + 1, ValueVector(N + 1));
F.resize(K + 1, ValueVector(N + 1));
Min.resize(K + 1, ValueVector(N + 1, std::numeric_limits<valueType>::max()));
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);
if (isChain()) {
Chain::solve();
return 0;
}
build(1, 0);
for (valueType m = 0; m < M; ++m) {
valueType u, v;
std::cin >> u >> v;
std::cout << solve(u, v) << '\n';
}
std::cout << std::flush;
return 0;
}
B. 定价
贪心策略是显然的,考虑如何维护。
发现我们每次只需要维护当前数不可能为 \(1\) 的且在前一个数出现过的位置开始向高位考虑即可,不难发现每次插入的位置满足单调性,因此可以使用栈来维护为 \(1\) 的位置。
考虑如何寻找位置,当当前位被置为 \(1\) 时,找到这一位置上第一个不为 \(1\) 的位置即可,这可以通过 std::bitset
来实现。
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::vector<ValueSet> SetVector;
typedef std::tuple<valueType, valueType, valueType> ValueTuple;
typedef std::vector<ValueTuple> TupleVector;
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;
constexpr valueType V = 500000, MAXN = 1005;
typedef std::bitset<V> Bitset;
typedef std::vector<std::bitset<MAXN>> BitMatrix;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cout.tie(nullptr);
#ifndef LOCAL_STDIO
freopen("price.in", "r", stdin);
freopen("price.out", "w", stdout);
#endif
valueType N, M, Q;
std::cin >> N >> M >> Q;
TupleVector oper(Q);
ValueVector pool;
for (auto &[type, r, c] : oper) {
std::cin >> type;
if (type == 2)
continue;
std::cin >> r >> c;
pool.emplace_back(c);
}
std::sort(pool.begin(), pool.end());
pool.erase(std::unique(pool.begin(), pool.end()), pool.end());
ValueVector power = pool;
for (auto &c : power)
c = pow(2, M - c);
for (auto &[type, r, c] : oper) {
if (type == 2)
continue;
c = std::distance(pool.begin(), std::lower_bound(pool.begin(), pool.end(), c));
}
BitMatrix Empty(pool.size());
for (auto &bit : Empty)
bit.set();
SetVector S(N + 1);
ValueMatrix G(N + 2);
auto Query = [&]() -> valueType {
ValueVector stack;
stack.reserve(N);
Bitset inStack;
inStack.reset();
valueType result = 0, sum = 0;
for (valueType i = 1; i <= N; ++i) {
valueType pos = pool.size();
for (auto const &c : G[i])
if (Empty[c][i] && inStack[c])
pos = std::min(pos, c);
G[i].clear();
if (S[i].lower_bound(pos) == S[i].begin())
return -1;
auto iter = std::make_reverse_iterator(S[i].lower_bound(pos));
bool flag = false;
while (iter != S[i].rend()) {
auto const &c = *iter;
if (!inStack[c]) {
while (!stack.empty() && stack.back() > c) {
inStack.reset(stack.back());
Dec(sum, power[stack.back()]);
stack.pop_back();
}
stack.emplace_back(c);
inStack.set(c);
Inc(sum, power[c]);
Inc(result, sum);
G[Empty[c]._Find_next(i)].emplace_back(c);
flag = true;
break;
}
++iter;
}
if (!flag)
return -1;
}
return result;
};
for (auto const &[type, r, c] : oper) {
if (type == 1) {
if (Empty[c][r]) {
Empty[c].reset(r);
S[r].insert(c);
} else {
Empty[c].set(r);
S[r].erase(c);
}
} else {
std::cout << Query() << '\n';
}
}
return 0;
}